package ptolemy.domains.pthales.lib;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import ptolemy.actor.IORelation;
import ptolemy.actor.Receiver;
import ptolemy.actor.TypeEvent;
import ptolemy.actor.TypeListener;
import ptolemy.actor.TypedIOPort;
import ptolemy.data.expr.Parameter;
import ptolemy.data.type.BaseType;
import ptolemy.data.type.StructuredType;
import ptolemy.data.type.Type;
import ptolemy.graph.Inequality;
import ptolemy.graph.InequalityTerm;
import ptolemy.kernel.ComponentEntity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.Workspace;
public class PThalesIOPort extends TypedIOPort {
/** Construct a PThalesIOPort with no container and no name that is
* neither an input nor an output.
*/
public PThalesIOPort(ComponentEntity container, String name,
boolean isInput, boolean isOutput) throws IllegalActionException,
NameDuplicationException {
super(container, name, isInput, isOutput);
//FIXME : adapt to correct type
setTypeEquals(BaseType.FLOAT);
// Add parameters for PThales Domain
initialize();
}
/** Construct a PThalesIOPort with a containing actor and a name
* that is neither an input nor an output. The specified container
* must implement the TypedActor 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
* TypedActor interface.
* @exception NameDuplicationException If the name coincides with
* a port already in the container.
*/
public PThalesIOPort(ComponentEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
//FIXME : adapt to correct type
setTypeEquals(BaseType.FLOAT);
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** If the port is an output, return the remote receivers that can
* receive from the port. For an output
* port, the returned value is an array of arrays of the same form
* as that returned by getReceivers() with no arguments. The length
* of the array is the width of the port (the number of channels).
* It is an array of arrays, each of which represents a group of
* receivers that receive data from the same channel.
* <p>
* This method may have the effect of creating new receivers in the
* remote input ports, if they do not already have the right number of
* receivers. In this case, previous receivers are lost, together
* with any data they may contain.
* <p>
* This method is read-synchronized on the workspace.
* @return The receivers for output data, or an empty array if there
* are none.
* @exception IllegalActionException
*/
public Receiver[][] getRemoteReceivers() throws IllegalActionException {
try {
_workspace.getReadAccess();
if (!isOutput()) {
return _EMPTY_RECEIVER_ARRAY;
}
int width = getWidth();
if (width <= 0) {
return _EMPTY_RECEIVER_ARRAY;
}
// For opaque port, try the cached _farReceivers
// Check validity of cached version
if (isOpaque() && (_farReceiversVersion == _workspace.getVersion())) {
return _farReceivers;
}
// If not an opaque port or Cache is not valid. Reconstruct it.
Receiver[][] farReceivers = new Receiver[width][0];
Iterator<?> relations = linkedRelationList().iterator();
int index = 0;
// Hypothese : 1 input per relation
while (relations.hasNext()) {
IORelation relation = (IORelation) relations.next();
// A null link (supported since indexed links) might
// yield a null relation here. EAL 7/19/00.
if (relation != null) {
Receiver[][] deepReceivers = relation.deepReceivers(this);
if (deepReceivers != null) {
for (int i = 0; i < deepReceivers.length; i++) {
farReceivers[index] = deepReceivers[i];
index++;
}
} else {
// create a number of null entries in farReceivers
// corresponding to the width of relation r
index += relation.getWidth();
}
}
}
// For an opaque port, cache the result.
if (isOpaque()) {
_farReceiversVersion = _workspace.getVersion();
_farReceivers = farReceivers;
}
return farReceivers;
} finally {
_workspace.doneReading();
}
}
/** Clone this port into the specified workspace. The new port is
* <i>not</i> added to the directory of that workspace (you must
* do this yourself if you want it there).
* The result is a new port with no connections and no container.
* The new port will have the same type as this one, but will not
* have any type listeners and type constraints attached to it.
*
* @param workspace The workspace for the cloned object.
* @exception CloneNotSupportedException If one or more of the
* attributes cannot be cloned.
* @return A new TypedIOPort.
*/
public Object clone(Workspace workspace) throws CloneNotSupportedException {
PThalesIOPort newObject = (PThalesIOPort) super.clone(workspace);
// set _declaredType and _resolvedType
if (_declaredType instanceof StructuredType
&& !_declaredType.isConstant()) {
newObject._declaredType = (Type) ((StructuredType) _declaredType)
.clone();
newObject._resolvedType = newObject._declaredType;
}
newObject._typeTerm = null;
newObject._typeListeners = new LinkedList<TypeListener>();
newObject._constraints = new HashSet<Inequality>();
return newObject;
}
/** Computes pattern size in byte, not the space in memory
* @return Pattern size in byte.
*/
public int getPatternSize() {
int valuePattern = 0;
pattern = (Parameter) getAttribute("pattern");
if (pattern != null) {
int value = 1;
String[] dims = pattern.toString().split(",");
for (String dim : dims) {
value *= Integer.parseInt(dim.split("=")[1].split("\\.")[0]
.trim());
}
valuePattern = value;
}
int sizeRepetition = 1;
tiling = (Parameter) getAttribute("tiling");
String str = ((PThalesGenericActor) getContainer()).getInternalRepetitions();
if (!str.equals(""))
{
String[] list = str.trim().split(",");
Integer[] rep = new Integer[list.length];
String[] til = new String[list.length];
for (int i = 0; i < list.length; i++) {
rep[i] = new Integer(list[i].trim());
til[i] = tiling.getExpression().split(",")[i].split("=")[1].trim();
}
// size for tiling dimensions
for (int i = 0; i < rep.length; i++) {
// Dimension added to pattern list
if (!til[i].equals("0"))
sizeRepetition *= rep[i]*Integer.parseInt(til[i]);
}
}
return valuePattern * sizeRepetition * getNbTokenPerData();
}
/** Computes pattern size in byte, not the space in memory
* @return Pattern size in byte.
*/
public int[] getPattern() {
int[] result = null;
Integer[] rep = new Integer[0];
String[] til = new String[0];
int numDim = 0;
pattern = (Parameter) getAttribute("pattern");
tiling = (Parameter) getAttribute("tiling");
String str = ((PThalesGenericActor) getContainer()).getInternalRepetitions();
if (!str.equals(""))
{
String[] list = str.trim().split(",");
rep = new Integer[list.length];
til = new String[list.length];
for (int i = 0; i < list.length; i++) {
rep[i] = new Integer(list[i].trim());
til[i] = tiling.getExpression().split(",")[i].split("=")[1].trim();
}
}
if (pattern != null) {
String[] dims = pattern.toString().split(",");
result = new int[dims.length + rep.length];
for (String dim : dims) {
result[numDim] = Integer.parseInt(dim.split("=")[1]
.split("\\.")[0].trim());
numDim++;
}
}
// size for tiling dimensions
for (int i = 0; i < rep.length; i++) {
// Dimension added to pattern list
if (!til[i].equals("0"))
result[numDim] = rep[i]*Integer.parseInt(til[i]);
numDim++;
}
return result;
}
/** Check if data type is a structure.
* If yes, gives the number of tokens needed to store all the data
* By default, the return value is 1
* @return the number of token needed to store the values
*/
public int getNbTokenPerData() {
if (_dataType.equals("Cplfloat") || _dataType.equals("Cpldouble")) {
return 2;
}
return 1;
}
/** Attribute update
*/
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if (attribute.getName().equals("dataType")) {
_dataType = ((Parameter) attribute).getExpression();
}
if (attribute.getName().equals("dataTypeSize")) {
_dataTypeSize = Integer.parseInt(((Parameter) attribute)
.getExpression());
}
}
///////////////////////////////////////////////////////////////////
//// ports and parameters ////
/** Reset the variable part of this type to the specified type.
* @exception IllegalActionException If the type is not settable,
* or the argument is not a Type.
*/
private void initialize() throws IllegalActionException,
NameDuplicationException {
base = new Parameter(this, "base");
base.setExpression("");
base.setTypeEquals(BaseType.STRING);
pattern = new Parameter(this, "pattern");
pattern.setExpression("");
pattern.setTypeEquals(BaseType.STRING);
tiling = new Parameter(this, "tiling");
tiling.setExpression("");
tiling.setTypeEquals(BaseType.STRING);
size = new Parameter(this, "size");
size.setExpression("");
size.setTypeEquals(BaseType.STRING);
dataType = new Parameter(this, "dataType");
dataType.setExpression("");
dataType.setTypeEquals(BaseType.STRING);
dataTypeSize = new Parameter(this, "dataTypeSize");
dataTypeSize.setExpression("");
dataTypeSize.setTypeEquals(BaseType.INT);
dimensionNames = new Parameter(this, "dimensionNames");
dimensionNames.setExpression("");
dimensionNames.setTypeEquals(BaseType.STRING);
}
/** Array base
*/
public Parameter base;
/** Array pattern
*/
public Parameter pattern;
/** Array tiling
*/
public Parameter tiling;
/** Array size
*/
public Parameter size;
/** data type (for code generation only)
*/
public Parameter dataType;
/** data type size(for code generation only)
*/
public Parameter dataTypeSize;
/** data type size(for code generation only)
*/
public Parameter dimensionNames;
/** Initialize the iteration counter. A derived class must call
* this method in its initialize() method or the <i>firingCountLimit</i>
* feature will not work.
* @exception IllegalActionException If the parent class throws it,
* which could occur if, for example, the director will not accept
* sequence actors.
*/
///////////////////////////////////////////////////////////////////
//// private methods ////
// Notify the type listener about type change.
private void _notifyTypeListener(Type oldType, Type newType) {
if (_typeListeners.size() > 0) {
TypeEvent event = new TypeEvent(this, oldType, newType);
Iterator<TypeListener> listeners = _typeListeners.iterator();
while (listeners.hasNext()) {
(listeners.next()).typeChanged(event);
}
}
}
///////////////////////////////////////////////////////////////////
//// protected variables ////
/** This is the value in parameter base.
*/
protected String _dataType = "";
protected int _dataTypeSize = 0;
///////////////////////////////////////////////////////////////////
//// private variables ////
private Type _declaredType = BaseType.UNKNOWN;
private Type _resolvedType = BaseType.UNKNOWN;
private TypeTerm _typeTerm = null;
// Listeners for type change.
private List<TypeListener> _typeListeners = new LinkedList<TypeListener>();
// type constraints
private Set<Inequality> _constraints = new HashSet<Inequality>();
/** To avoid creating this repeatedly, we use a single version. */
private static final Receiver[][] _EMPTY_RECEIVER_ARRAY = new Receiver[0][0];
// A cache of the deeply connected Receivers, and the versions.
// 'transient' means that the variable will not be serialized.
private transient Receiver[][] _farReceivers;
private transient long _farReceiversVersion = -1;
///////////////////////////////////////////////////////////////////
//// inner classes ////
private class TypeTerm implements InequalityTerm {
///////////////////////////////////////////////////////////////
//// public inner methods ////
/** Return this TypedIOPort.
* @return A TypedIOPort.
*/
public Object getAssociatedObject() {
return PThalesIOPort.this;
}
/** Return the type of this TypedIOPort.
*/
public Object getValue() {
return getType();
}
/** Return this TypeTerm in an array if this term represent
* a type variable. This term represents a type variable
* if the type of this port is not set through setTypeEquals().
* If the type of this port is set, return an array of size zero.
* @return An array of InequalityTerm.
*/
public InequalityTerm[] getVariables() {
if (isSettable()) {
InequalityTerm[] variable = new InequalityTerm[1];
variable[0] = this;
return variable;
}
return (new InequalityTerm[0]);
}
/** Reset the variable part of this type to the specified type.
* @param type A Type.
* @exception IllegalActionException If the type is not settable,
* or the argument is not a Type.
*/
public void initialize(Object type) throws IllegalActionException {
if (!isSettable()) {
throw new IllegalActionException("TypeTerm.initialize: "
+ "Cannot initialize a constant type.");
}
if (!(type instanceof Type)) {
throw new IllegalActionException("TypeTerm.initialize: "
+ "The argument is not a Type.");
}
Type oldType = _resolvedType;
if (_declaredType == BaseType.UNKNOWN) {
_resolvedType = (Type) type;
} else {
// _declaredType is a StructuredType
((StructuredType) _resolvedType).initialize((Type) type);
}
if (!oldType.equals(_resolvedType)) {
_notifyTypeListener(oldType, _resolvedType);
}
}
/** Test if the type of this TypedIOPort can be changed.
* The type can be changed if setTypeEquals() is not called,
* or called with a BaseType.UNKNOWN argument.
* @return True if the type of this TypedIOPort can be changed;
* false otherwise.
*/
public boolean isSettable() {
return !_declaredType.isConstant();
}
/** Check whether the current value of this term is acceptable.
* This method delegates the check to the isTypeAcceptable()
* method of the outer class.
* @return True if the current value is acceptable.
*/
public boolean isValueAcceptable() {
return isTypeAcceptable();
}
/** Set the type of this port.
* @param type A Type.
* @exception IllegalActionException If the new type violates
* the declared type of this port.
*/
public void setValue(Object type) throws IllegalActionException {
if (!isSettable()) {
throw new IllegalActionException(
"TypedIOPort$TypeTerm.setValue: The type is not "
+ "settable.");
}
if (!_declaredType.isSubstitutionInstance((Type) type)) {
throw new IllegalActionException("Type conflict on port "
+ PThalesIOPort.this.getFullName() + ".\n"
+ "Declared type is " + _declaredType.toString()
+ ".\n"
+ "The connection or type constraints, however, "
+ "require type " + type.toString());
}
Type oldType = _resolvedType;
if (_declaredType == BaseType.UNKNOWN) {
_resolvedType = (Type) type;
} else {
// _declaredType is a StructuredType
((StructuredType) _resolvedType)
.updateType((StructuredType) type);
}
if (!oldType.equals(type)) {
_notifyTypeListener(oldType, _resolvedType);
}
}
/** Override the base class to give a description of the port
* and its type.
* @return A description of the port and its type.
*/
public String toString() {
return "(" + PThalesIOPort.this.toString() + ", " + getType() + ")";
}
}
}