Skip to content

Commit

Permalink
Update Connection to be a new ArrayBasedValue.
Browse files Browse the repository at this point in the history
  • Loading branch information
neilcsmith-net committed Jun 11, 2024
1 parent 5fc76e0 commit 5495bb8
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 130 deletions.
111 changes: 57 additions & 54 deletions praxiscore-api/src/main/java/org/praxislive/core/Connection.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2023 Neil C Smith.
* Copyright 2024 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
Expand All @@ -21,99 +21,102 @@
*/
package org.praxislive.core;

import org.praxislive.core.protocols.ContainerProtocol;
import java.util.Optional;
import org.praxislive.core.types.PArray;
import org.praxislive.core.types.PString;

/**
* A type representing a connection between two ports.
*/
public final class Connection {

private final PArray dataArray;
public final class Connection extends PArray.ArrayBasedValue {

/**
* Create a connection reference. The child IDs must be valid according to
* {@link ComponentAddress#isValidID(java.lang.String)}. The port IDs must
* be valid according to {@link PortAddress#isValidID(java.lang.String)}.
*
* @param child1 ID of first child
* @param port1 ID of port on first child
* @param child2 ID of second child
* @param port2 ID of port on second child
* @throws IllegalArgumentException if the IDs are not valid
* Value type name.
*/
public Connection(String child1, String port1, String child2, String port2) {
verifyChildID(child1);
verifyChildID(child2);
verifyPortID(port1);
verifyPortID(port2);
dataArray = PArray.of(PString.of(child1), PString.of(port1),
PString.of(child2), PString.of(port2));
public static final String TYPE_NAME = "Connection";

private Connection(PArray data) {
super(data);
if (data.size() != 4) {
throw new IllegalArgumentException("Invalid connection data");
}
verifyChildID(data.get(0).toString());
verifyPortID(data.get(1).toString());
verifyChildID(data.get(2).toString());
verifyPortID(data.get(3).toString());
}

/**
* Query the component ID of the first connected component.
* Query the component ID of the source component.
*
* @return ID of first child
*/
public String child1() {
return dataArray.get(0).toString();
public String sourceComponent() {
return dataArray().get(0).toString();
}

/**
* Query the port ID of the connected port on the first component.
* Query the port ID of the source port.
*
* @return ID of port on first child
*/
public String port1() {
return dataArray.get(1).toString();
public String sourcePort() {
return dataArray().get(1).toString();
}

/**
* Query the component ID of the second connected component.
* Query the component ID of the target component.
*
* @return ID of the second child
*/
public String child2() {
return dataArray.get(2).toString();
public String targetComponent() {
return dataArray().get(2).toString();
}

/**
* Query the port ID of the connected port on the second component.
* Query the port ID of the target port.
*
* @return ID of port on second child
*/
public String port2() {
return dataArray.get(3).toString();
public String targetPort() {
return dataArray().get(3).toString();
}

/**
* Access the Connection as the backing PArray data. The data consists of
* four values, {@code child1 port1 child2 port2}.
* <p>
* This is the same format included in the list returned from
* {@link ContainerProtocol#CONNECTIONS}.
* Create a connection reference. The child IDs must be valid according to
* {@link ComponentAddress#isValidID(java.lang.String)}. The port IDs must
* be valid according to {@link PortAddress#isValidID(java.lang.String)}.
*
* @return backing data array
* @param child1 ID of first child
* @param port1 ID of port on first child
* @param child2 ID of second child
* @param port2 ID of port on second child
* @return new connection
* @throws IllegalArgumentException if the IDs are not valid
*/
public PArray dataArray() {
return dataArray;
}

@Override
public String toString() {
return dataArray.toString();
}

@Override
public int hashCode() {
return dataArray.hashCode();
public static Connection of(String child1, String port1, String child2, String port2) {
return new Connection(PArray.of(
PString.of(child1), PString.of(port1),
PString.of(child2), PString.of(port2)
));
}

@Override
public boolean equals(Object obj) {
return obj == this || (obj instanceof Connection c && dataArray.equals(c.dataArray));
/**
* Coerce the provided value into a Connection if possible.
*
* @param value value of unknown type
* @return connection or empty optional
*/
public static Optional<Connection> from(Value value) {
if (value instanceof Connection connection) {
return Optional.of(connection);
} else {
try {
return PArray.from(value).map(Connection::new);
} catch (Exception ex) {
return Optional.empty();
}
}
}

private static void verifyChildID(String childID) {
Expand Down
3 changes: 2 additions & 1 deletion praxiscore-api/src/main/java/org/praxislive/core/Value.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2022 Neil C Smith.
* Copyright 2024 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
Expand Down Expand Up @@ -320,6 +320,7 @@ public static List<Type<?>> listAll() {
types.add(new Type<>(PortAddress.class, PortAddress.TYPE_NAME, PortAddress::from));

types.add(new Type<>(ComponentType.class, ComponentType.TYPE_NAME, ComponentType::from));
types.add(new Type<>(Connection.class, Connection.TYPE_NAME, Connection::from));

Map<Class<? extends Value>, Type<?>> typesByClass = new HashMap<>();
Map<String, Type<?>> typesByName = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2023 Neil C Smith.
* Copyright 2024 Neil C Smith.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3 only, as
Expand All @@ -25,6 +25,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Stream;
Expand Down Expand Up @@ -349,4 +350,78 @@ public static ArgumentInfo info() {
);
}

/**
* An abstract superclass for values that are backed solely by a PArray.
* Subclassing this type can help with efficient serialization of the
* underlying representation. The concrete value type must be able to
* construct an equivalent value entirely from the PArray returned from
* {@link #dataArray()}.
* <p>
* The toString, equals, equivalent and hashCode methods are all implemented
* based solely on the array data.
*/
public static abstract class ArrayBasedValue extends Value {

private final PArray data;

/**
* Construct a MapBasedValue using the provided data map.
*
* @param data data map
*/
protected ArrayBasedValue(PArray data) {
this.data = Objects.requireNonNull(data);
}

/**
* Access the backing PArray data.
*
* @return backing array
*/
public final PArray dataArray() {
return data;
}

@Override
public final String toString() {
return data.toString();
}

@Override
public final int hashCode() {
return data.hashCode();
}

@Override
public final boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArrayBasedValue other = (ArrayBasedValue) obj;
return Objects.equals(this.data, other.data);
}

@Override
public final boolean equivalent(Value value) {
if (this == value) {
return true;
}
if (value instanceof ArrayBasedValue arrayBased) {
return data.equivalent(arrayBased.data);
} else {
return PMap.from(value)
.map(data::equivalent)
.orElse(false);
}

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public abstract class AbstractContainer extends AbstractComponent implements Con
private final static System.Logger LOG = System.getLogger(AbstractContainer.class.getName());

private final Map<String, Component> childMap;
private final Set<PArray> connections;
private final Set<Connection> connections;

protected AbstractContainer() {
childMap = new LinkedHashMap<>();
Expand Down Expand Up @@ -124,9 +124,7 @@ protected final void writeChildren(TreeWriter writer) {
}

protected final void writeConnections(TreeWriter writer) {
connections.forEach(c -> writer.writeConnection(
new Connection(c.get(0).toString(), c.get(1).toString(),
c.get(2).toString(), c.get(3).toString())));
connections.forEach(writer::writeConnection);
}

protected void addChild(String id, Component child) throws VetoException {
Expand Down Expand Up @@ -173,34 +171,26 @@ protected String getChildID(Component child) {

protected void connect(String component1, String port1, String component2, String port2)
throws PortConnectionException {
handleConnection(true,
PString.of(component1),
PString.of(port1),
PString.of(component2),
PString.of(port2));
handleConnection(true, component1, port1, component2, port2);
}

protected void disconnect(String component1, String port1, String component2, String port2) {
try {
handleConnection(false,
PString.of(component1),
PString.of(port1),
PString.of(component2),
PString.of(port2));
handleConnection(false, component1, port1, component2, port2);
} catch (PortConnectionException ex) {
LOG.log(System.Logger.Level.ERROR, "", ex);
}
}

private void handleConnection(boolean connect, PString c1id, PString p1id, PString c2id, PString p2id)
private void handleConnection(boolean connect, String component1, String port1, String component2, String port2)
throws PortConnectionException {
try {
Component c1 = getChild(c1id.toString());
final Port p1 = c1.getPort(p1id.toString());
Component c2 = getChild(c2id.toString());
final Port p2 = c2.getPort(p2id.toString());
Component c1 = getChild(component1);
final Port p1 = c1.getPort(port1);
Component c2 = getChild(component2);
final Port p2 = c2.getPort(port2);

final PArray connection = PArray.of(c1id, p1id, c2id, p2id);
final Connection connection = Connection.of(component1, port1, component2, port2);

if (connect) {
p1.connect(p2);
Expand All @@ -214,8 +204,8 @@ private void handleConnection(boolean connect, PString c1id, PString p1id, PStri
}
} catch (Exception ex) {
LOG.log(System.Logger.Level.DEBUG, "Can't connect ports.", ex);
throw new PortConnectionException("Can't connect " + c1id + "!" + p1id
+ " to " + c2id + "!" + p2id);
throw new PortConnectionException("Can't connect " + component1 + "!" + port1
+ " to " + component2 + "!" + port2);
}
}

Expand Down Expand Up @@ -277,10 +267,10 @@ protected class ConnectControl implements Control {
@Override
public void call(Call call, PacketRouter router) throws Exception {
handleConnection(true,
PString.from(call.args().get(0)).orElseThrow(),
PString.from(call.args().get(1)).orElseThrow(),
PString.from(call.args().get(2)).orElseThrow(),
PString.from(call.args().get(3)).orElseThrow());
call.args().get(0).toString(),
call.args().get(1).toString(),
call.args().get(2).toString(),
call.args().get(3).toString());
router.route(call.reply());
}

Expand All @@ -291,10 +281,10 @@ protected class DisconnectControl implements Control {
@Override
public void call(Call call, PacketRouter router) throws Exception {
handleConnection(false,
PString.from(call.args().get(0)).orElseThrow(),
PString.from(call.args().get(1)).orElseThrow(),
PString.from(call.args().get(2)).orElseThrow(),
PString.from(call.args().get(3)).orElseThrow());
call.args().get(0).toString(),
call.args().get(1).toString(),
call.args().get(2).toString(),
call.args().get(3).toString());
router.route(call.reply());
}

Expand All @@ -314,9 +304,9 @@ private class ConnectionListener implements PortListener {

Port p1;
Port p2;
PArray connection;
Connection connection;

private ConnectionListener(Port p1, Port p2, PArray connection) {
private ConnectionListener(Port p1, Port p2, Connection connection) {
this.p1 = p1;
this.p2 = p2;
this.connection = connection;
Expand Down
Loading

0 comments on commit 5495bb8

Please sign in to comment.