package ptolemy.apps.apes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import ptolemy.actor.Actor;
import ptolemy.actor.CompositeActor;
import ptolemy.actor.IOPort;
import ptolemy.actor.NoRoomException;
import ptolemy.actor.Receiver;
import ptolemy.actor.TypedIOPort;
import ptolemy.data.Token;
import ptolemy.data.expr.StringParameter;
import ptolemy.kernel.ComponentEntity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.Workspace;
//////////////////////////////////////////////////////////////////////////
//// MulticastIOPort
/**
* TODO dependent on the parameters determine if port is an input/output/io port
*
* This port communicates without wired connections. The port references the
* destinations and sources by names as specified by the <i>destinationActors</i>
* or <i>sourceActors</i> parameter.
* <p>
* A port is outside multicast if at least one destination actor is given and a
* NamedDestinationActor with the given name is contained by the container of
* the port's container (transparent hierarchy is not supported). A port is
* inside multicast if at least one destination actor is given and a
* NamedDestinationActor with the given name is contained by the container of
* this port. If no destination actor is given, then the behavior of the port
* reverts to that of the base class. Specifically, it will only communicate if
* it is wired. If at least one destination is given and the actor exists, then
* all the wired connections to this port are ignored.
* <p>
* The width of this port on either side that is using multicast communication
* is fixed at one. Otherwise, it depends on the number of links to the port.
* <p>
* When this port is used for multicast communications, nothing is connected to
* it. Consequently, methods that access the topology such as
* connectedPortList() and deepConnectedInPortList() return an empty list. There
* are no deeply connected ports.
*
* TODO: How about sinkPortList()?
*
* @author Patricia Derler, Stefan Resmerita
*/
public class MulticastIOPort extends TypedIOPort {
/**
* Construct a port in the specified workspace with an empty string as a
* name. You can then change the name with setName(). If the workspace
* argument is null, then use the default workspace. The object is added to
* the workspace directory. Increment the version number of the workspace.
*
* @param workspace
* The workspace that will list the port.
* @exception IllegalActionException
* If creating the parameters of this port throws it.
* @exception NameDuplicationException
* If creating the parameters of this port throws it.
*/
public MulticastIOPort(Workspace workspace) throws IllegalActionException,
NameDuplicationException {
super(workspace);
_initialize();
}
/**
* Construct a port with the specified container and name that is neither an
* input nor an output. The specified container must implement the Actor
* interface, or an exception will be thrown.
*
* @param container
* The container actor.
* @param name
* The name of the port.
* @exception IllegalActionException
* If the port is not of an acceptable class for the
* container, or if the container does not implement the
* Actor interface.
* @exception NameDuplicationException
* If the name coincides with a port already in the
* container.
*/
public MulticastIOPort(ComponentEntity container, String name)
throws IllegalActionException, NameDuplicationException {
this(container, name, false, false);
_initialize();
}
/**
* Construct a port with a container and a name that is either an input, an
* output, or both, depending on the third and fourth arguments. The
* specified container must implement the Actor interface or an exception
* will be thrown.
*
* @param container
* The container actor.
* @param name
* The name of the port.
* @param isInput
* True if this is to be an input port.
* @param isOutput
* True if this is to be an output port.
* @exception IllegalActionException
* If the port is not of an acceptable class for the
* container, or if the container does not implement the
* Actor interface.
* @exception NameDuplicationException
* If the name coincides with a port already in the
* container.
*/
public MulticastIOPort(ComponentEntity container, String name,
boolean isInput, boolean isOutput) throws IllegalActionException,
NameDuplicationException {
super(container, name, isInput, isOutput);
_initialize();
}
///////////////////////////////////////////////////////////////////
//// parameters ////
/**
* A comma-separated list of actors that can send tokens to this port
* through multicast ports. This is a string that defaults to the * string,
* indicating that all actors are accepted.
*/
public StringParameter sourceActors;
/**
* A comma-separated list of actors tokens are sent to by this port.
* This is a string that defaults to the * string,
* indicating that all actors are accepted.
*/
public StringParameter destinationActors;
///////////////////////////////////////////////////////////////////
//// public methods ////
/**
* If the attribute is one of the properties attributes, make sure its value
* is a record token.
*
* @param attribute
* The attribute that changed.
* @exception IllegalActionException
* If the change is not acceptable to this container.
*/
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if ((attribute == destinationActors) || (attribute == sourceActors)) {
// Since the source/destination parameters affect connectivity, we should
// treat changes to their values as changes to the topology.
// To do that, we listen for changes and increment the version
// number of the workspace.
workspace().incrVersion();
//TODO: tokenizer for comma separated string
_destinationActorNames.add(destinationActors.stringValue());
} else {
super.attributeChanged(attribute);
}
}
/**
* Send token to every actor with a multicastIOPort
* in the same hierarchy in the model.
*
* @param token
* The token to send.
*/
public void broadcast(Token token) throws IllegalActionException {
Collection<IOPort> destinationActorPorts = _getAllDestinationActors();
if (destinationActorPorts != null) {
for (IOPort port : destinationActorPorts) {
Receiver[][] receivers = port.getReceivers();
for (int i = 0; i < receivers.length; i++) {
for (int j = 0; j < receivers[0].length; j++) {
Receiver receiver = receivers[i][j];
receiver.put(token);
}
}
}
} else {
super.broadcast(token);
}
}
/**
* Send the specified portion of a token array to all receivers connected to
* this port. The first <i>vectorLength</i> tokens of the token array are
* sent.
*
* @param tokenArray
* The token array to send
* @param vectorLength
* The number of elements of the token array to send.
* @exception NoRoomException
* If there is no room in the receiver.
* @exception IllegalActionException
* If the tokens to be sent cannot be converted to the type
* of this port
*/
public void broadcast(Token[] tokenArray, int vectorLength)
throws IllegalActionException, NoRoomException {
// TODO change to take into account the destination list
super.broadcast(tokenArray, vectorLength);
}
/**
* Clone the object into the specified workspace. The new object is <i>not</i>
* added to the directory of that workspace (you must do this yourself if
* you want it there).
*
* @param workspace
* The workspace for the cloned object.
* @exception CloneNotSupportedException
* Not thrown in this base class
* @return The new Attribute.
*/
public Object clone(Workspace workspace) throws CloneNotSupportedException {
MulticastIOPort newObject = (MulticastIOPort) super.clone(workspace);
newObject._receivers = null;
newObject._insideReceivers = null;
return newObject;
}
/**
* Override the base class to create receivers for MulticastIOPort.
*
* @exception IllegalActionException
* If this port is not an opaque input port or if there is no
* director.
*/
public void createReceivers() throws IllegalActionException {
// This call will create receivers based on relations that
// are linked to the port.
super.createReceivers();
_receivers = new Receiver[1][1];
_receivers[0][0] = _newReceiver();
if (getContainer() instanceof CompositeActor) {
_insideReceivers = new Receiver[1][1];
_insideReceivers[0][0] = _newInsideReceiver();
}
}
/**
* Override the base class to return the inside receiver for wireless
* communication if wireless communication is being used. Otherwise, defer
* to the base class.
*
* @return The local inside receivers, or an empty array if there are none.
*/
public Receiver[][] getInsideReceivers() {
if (getContainer() instanceof CompositeActor) {
return _insideReceivers;
} else {
return super.getInsideReceivers();
}
}
/**
* Override the base class to return the outside receiver for wireless
* communication if wireless communication is being used. Otherwise, defer
* to the base class.
*
* @return The local receivers, or an empty array if there are none.
*/
public Receiver[][] getReceivers() {
// if (getOutsideChannel() != null) {
if (_receivers == null) {
return _EMPTY_RECEIVERS;
}
return _receivers;
// } else {
// return super.getReceivers();
// }
}
/**
* Send Token to specified destination actors. Note that
* the channelIndex is ignored as there is just one channel.
*/
public void send(int channelIndex, Token token)
throws IllegalActionException, NoRoomException {
Collection<IOPort> destinationActorPorts = _getDestinationActors();
if (destinationActorPorts != null && destinationActorPorts.size() > 0) {
for (IOPort port : destinationActorPorts) {
Receiver[][] receivers = port.getReceivers();
for (int i = 0; i < receivers.length; i++) {
for (int j = 0; j < receivers[0].length; j++) {
Receiver receiver = receivers[i][j];
receiver.put(token);
}
}
}
} else {
super.send(channelIndex, token);
}
}
/**
* Send to actors in the intersection of the list given by the port
* parameter and the list given in the argument of the method.
*/
public void send(List<String> destinations, Token token)
throws IllegalActionException, NoRoomException {
if (!_destinationActorNames.isEmpty()) {
for (String actorName : destinations) {
if (_destinationActorNames.contains("*")
|| _destinationActorNames.contains(actorName)) {
if (!_destinationActorPorts.containsKey(actorName)
|| workspace().getVersion() != _destinationActorsVersion) {
CompositeActor compositeActor = (CompositeActor) getContainer()
.getContainer();
Object entity = compositeActor.getEntity(actorName);
if (entity instanceof Actor) {
Actor actor = (Actor) entity;
List<IOPort> inputPorts = actor.inputPortList();
MulticastIOPort sinkPort = null;
for (IOPort port : inputPorts) {
if (port instanceof MulticastIOPort) {
sinkPort = (MulticastIOPort) port;
}
}
_destinationActorPorts.put(actorName, sinkPort);
}
_destinationActorsVersion = workspace().getVersion();
}
IOPort port = _destinationActorPorts.get(actorName);
Receiver[][] receivers = port.getReceivers();
for (int i = 0; i < receivers.length; i++) {
for (int j = 0; j < receivers[0].length; j++) {
Receiver receiver = receivers[i][j];
receiver.put(token);
}
}
}
}
}
}
/**
* Send token to actor given by actorName.
*/
public void send(String actorName, Token token)
throws IllegalActionException, NoRoomException {
if (!_destinationActorNames.isEmpty()) {
if (_destinationActorNames.contains("*")
|| _destinationActorNames.contains(actorName)) {
if (!_destinationActorPorts.containsKey(actorName)
|| workspace().getVersion() != _destinationActorsVersion) {
CompositeActor compositeActor = (CompositeActor) getContainer()
.getContainer();
Object entity = compositeActor.getEntity(actorName);
if (entity instanceof Actor) {
Actor actor = (Actor) entity;
List<IOPort> inputPorts = actor.inputPortList();
MulticastIOPort sinkPort = null;
for (IOPort port : inputPorts) {
if (port instanceof MulticastIOPort) {
sinkPort = (MulticastIOPort) port;
}
}
_destinationActorPorts.put(actorName, sinkPort);
}
_destinationActorsVersion = workspace().getVersion();
}
IOPort port = _destinationActorPorts.get(actorName);
if (port != null) {
Receiver[][] receivers = port.getReceivers();
for (int i = 0; i < receivers.length; i++) {
for (int j = 0; j < receivers[0].length; j++) {
Receiver receiver = receivers[i][j];
receiver.put(token);
}
}
}
}
}
}
/**
* Send token to actor.
*/
public void send(Actor actor, Token token) throws IllegalActionException,
NoRoomException {
if (!_destinationActorNames.isEmpty()) {
if (_destinationActorNames.contains("*")
|| _destinationActorNames.contains(actor.getName())) {
if (!_destinationActorPorts.containsKey(actor.getName())
|| workspace().getVersion() != _destinationActorsVersion) {
List<IOPort> inputPorts = actor.inputPortList();
MulticastIOPort sinkPort = null;
for (IOPort port : inputPorts) {
if (port instanceof MulticastIOPort) {
sinkPort = (MulticastIOPort) port;
}
}
_destinationActorPorts.put(actor.getName(), sinkPort);
}
_destinationActorsVersion = workspace().getVersion();
}
IOPort port = _destinationActorPorts.get(actor.getName());
if (port != null) {
Receiver[][] receivers = port.getReceivers();
for (int i = 0; i < receivers.length; i++) {
for (int j = 0; j < receivers[0].length; j++) {
Receiver receiver = receivers[i][j];
receiver.put(token);
}
}
}
}
}
public void send(Token token) throws IllegalActionException,
NoRoomException {
send(0, token);
}
private Collection<IOPort> _getDestinationActors() {
if (workspace().getVersion() != _destinationActorsVersion) {
// TODO catch exceptions if getContainer() fails
CompositeActor compositeActor = (CompositeActor) getContainer()
.getContainer();
List entities = compositeActor.entityList();
for (Iterator it = entities.iterator(); it.hasNext();) {
Object entity = it.next();
if (entity instanceof Actor) {
Actor actor = (Actor) entity;
if (_destinationActorNames.equals("*")
|| _destinationActorNames.contains(actor.getName())) {
List<IOPort> inputPorts = actor.inputPortList();
MulticastIOPort sinkPort = null;
for (IOPort port : inputPorts) {
if (port instanceof MulticastIOPort) {
sinkPort = (MulticastIOPort) port;
}
}
_destinationActorPorts.put(actor.getName(), sinkPort);
}
}
}
}
_destinationActorsVersion = workspace().getVersion();
return _destinationActorPorts.values();
}
private Collection<IOPort> _getAllDestinationActors() {
if (workspace().getVersion() != _destinationActorsVersion) {
// TODO catch exceptions if getContainer() fails
CompositeActor compositeActor = (CompositeActor) getContainer()
.getContainer();
List entities = compositeActor.entityList();
for (Iterator it = entities.iterator(); it.hasNext();) {
Object entity = it.next();
if (entity instanceof Actor) {
Actor actor = (Actor) entity;
List<IOPort> inputPorts = actor.inputPortList();
MulticastIOPort sinkPort = null;
for (IOPort port : inputPorts) {
if (port instanceof MulticastIOPort) {
sinkPort = (MulticastIOPort) port;
}
}
_destinationActorPorts.put(actor.getName(), sinkPort);
}
}
}
_destinationActorsVersion = workspace().getVersion();
return _destinationActorPorts.values();
}
private void _initialize() throws IllegalActionException,
NameDuplicationException {
destinationActors = new StringParameter(this, "destinationActors");
destinationActors.setExpression("");
_destinationActorNames = new ArrayList<String>();
_destinationActorPorts = new HashMap<String, IOPort>();
sourceActors = new StringParameter(this, "sourceActors");
sourceActors.setExpression("*");
}
///////////////////////////////////////////////////////////////////
//// private variables ////
// To ensure that getReceivers() and variants never return null.
private static Receiver[][] _EMPTY_RECEIVERS = new Receiver[0][0];
// Receivers for this port for outside wireless connections.
private Receiver[][] _receivers;
// Receivers for this port for inside wireless connections.
private Receiver[][] _insideReceivers;
/**
*/
private long _destinationActorsVersion;
private HashMap<String, IOPort> _destinationActorPorts;
private List<String> _destinationActorNames;
}