/* An actor for testing type resolution.
Copyright (c) 2003-2007 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above
copyright notice and the following two paragraphs appear in all copies
of this software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
PT_COPYRIGHT_VERSION_2
COPYRIGHTENDKEY
*/
package ptolemy.actor.lib;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import ptolemy.actor.TypedIOPort;
import ptolemy.data.BooleanToken;
import ptolemy.data.RecordToken;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.expr.Parameter;
import ptolemy.data.type.BaseType;
import ptolemy.kernel.ComponentEntity;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
//////////////////////////////////////////////////////////////////////////
//// TypeTest
/**
<p>An actor that can be used for regression test of the type resolution
system. During the initialize phase, after type resolution has been
performed, this actor compares resolved types in the model with types
stored in several parameters. If the types are the same, then the
actor does nothing. However, if the types are different, then the
actor throws an exception. Hence, as with the Test actor, this actor
can be easily used to build automatically executed tests of the type
system from within Vergil.
</p><p> The types expected by this actor are stored in two parameters,
each parameter contains a record of record of strings. The outer
record contains labels corresponding to the names of actors in the
same level of hierarchy as this actor. The inner record contains
labels corresponding to the names of typeable objects in the
appropriate actor. The strings in the record correspond to string
representations of the types of the typeable objects. For the
<i>portTypes</i> parameter, the typeable objects are assumed to be
ports, and for the <i>parameterTypes</i> parameter, the objects are
assumed to be parameters.
</p><p> Note that this actor only tests type resolution at one level of
opaque hierarchy. Hierarchical models should include multiple
instances of this actor. Since filling in the types manually is
difficult, this actor includes a training mode, similar to the
NonStrictTest actor. This mode automatically fills in the type
parameters. Also note that it is not necessary to specify the types
of all typeable objects. Any objects for which no type is specified
are not checked.
</p><p> During runtime, this actor consumes and ignores any input tokens.
This makes it very easy to add this actor to an existing model without
changing the behavior of the model.</p>
@author Steve Neuendorffer
@version $Id$
@see ptolemy.actor.lib.Test
@since Ptolemy II 3.0
@Pt.ProposedRating Red (eal)
@Pt.AcceptedRating Red (ssachs)
*/
public class TypeTest extends Discard {
/** Construct an actor with an input multiport.
* @param container The container.
* @param name The name of this actor.
* @exception IllegalActionException If the actor cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public TypeTest(CompositeEntity container, String name)
throws NameDuplicationException, IllegalActionException {
super(container, name);
parameterTypes = new Parameter(this, "parameterTypes");
parameterTypes.setExpression("");
// parameterTypes.setTypeEquals(
// new RecordType(new String[0], new Type[0]));
portTypes = new Parameter(this, "portTypes");
portTypes.setExpression("");
// portTypes.setTypeEquals(
// new RecordType(new String[0], new Type[0]));
trainingMode = new Parameter(this, "trainingMode");
trainingMode.setExpression("false");
trainingMode.setTypeEquals(BaseType.BOOLEAN);
}
///////////////////////////////////////////////////////////////////
//// ports and parameters ////
/** A record of record of strings representing the types of
* parameters of actors in the model.
*/
public Parameter parameterTypes;
/** A record of record of strings representing the types of ports
* of actors in the model.
*/
public Parameter portTypes;
/** If true, then do not check inputs, but rather collect them
* into the <i>portTypes</i> and <i>parameterTypes</i> arrays.
* This parameter is a boolean, and it defaults to false.
*/
public Parameter trainingMode;
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Initialize this actor. If the types stored in the type
* parameters do not correspond to the types of corresponding
* typeable objects in the model, then throw an exception
* indicating a failed regression test.
* @exception IllegalActionException If the test fails.
*/
public void initialize() throws IllegalActionException {
super.initialize();
// Accumulate the types of ports and Parameters
ArrayList portActorNameList = new ArrayList();
ArrayList parameterActorNameList = new ArrayList();
ArrayList portAssignments = new ArrayList();
ArrayList parameterAssignments = new ArrayList();
List entityList = ((CompositeEntity) getContainer()).entityList();
for (Iterator i = entityList.iterator(); i.hasNext();) {
ComponentEntity entity = (ComponentEntity) i.next();
// Skip the type test actor itself.
if (entity.equals(this)) {
continue;
}
ArrayList portNames = new ArrayList();
ArrayList portTypes = new ArrayList();
for (Iterator ports = entity.portList().iterator(); ports.hasNext();) {
TypedIOPort port = (TypedIOPort) ports.next();
portNames.add(port.getName());
portTypes.add(new StringToken(port.getType().toString()));
}
if (portNames.size() > 0) {
portActorNameList.add(entity.getName());
portAssignments.add(new RecordToken((String[]) portNames
.toArray(new String[portNames.size()]),
(Token[]) portTypes
.toArray(new Token[portTypes.size()])));
}
ArrayList paramNames = new ArrayList();
ArrayList paramTypes = new ArrayList();
for (Iterator params = entity.attributeList(Parameter.class)
.iterator(); params.hasNext();) {
Parameter param = (Parameter) params.next();
paramNames.add(param.getName());
paramTypes.add(new StringToken(param.getType().toString()));
}
if (paramNames.size() > 0) {
parameterActorNameList.add(entity.getName());
parameterAssignments.add(new RecordToken((String[]) paramNames
.toArray(new String[paramNames.size()]),
(Token[]) paramTypes.toArray(new Token[paramTypes
.size()])));
}
}
RecordToken actualPortTypes = new RecordToken(
(String[]) portActorNameList
.toArray(new String[portActorNameList.size()]),
(Token[]) portAssignments.toArray(new Token[portAssignments
.size()]));
RecordToken actualParameterTypes = new RecordToken(
(String[]) parameterActorNameList
.toArray(new String[parameterActorNameList.size()]),
(Token[]) parameterAssignments
.toArray(new Token[parameterAssignments.size()]));
if (((BooleanToken) trainingMode.getToken()).booleanValue()) {
if (NonStrictTest.isRunningNightlyBuild()) {
throw new IllegalActionException(this,
NonStrictTest.TRAINING_MODE_ERROR_MESSAGE);
} else {
System.err.println("Warning: '" + this.getFullName()
+ "' is in training mode, set the trainingMode "
+ "parameter to false before checking in");
}
if (actualPortTypes.length() > 0) {
portTypes.setToken(actualPortTypes);
} else {
portTypes.setToken((Token) null);
}
if (actualParameterTypes.length() > 0) {
parameterTypes.setToken(actualParameterTypes);
} else {
parameterTypes.setToken((Token) null);
}
} else {
RecordToken correctPortTypes = (RecordToken) portTypes.getToken();
RecordToken correctParameterTypes = (RecordToken) parameterTypes
.getToken();
if (correctPortTypes != null) {
for (Iterator actorNames = correctPortTypes.labelSet()
.iterator(); actorNames.hasNext();) {
String actorName = (String) actorNames.next();
RecordToken assignment = (RecordToken) correctPortTypes
.get(actorName);
for (Iterator names = assignment.labelSet().iterator(); names
.hasNext();) {
String name = (String) names.next();
StringToken value = (StringToken) assignment.get(name);
if (actualPortTypes.get(actorName) == null) {
throw new IllegalActionException(
this,
"actualPortTypes.get("
+ actorName
+ ") returned null. Perhaps there is no "
+ "actor by that name?");
}
StringToken actualValue = (StringToken) ((RecordToken) actualPortTypes
.get(actorName)).get(name);
if (!value.equals(actualValue)) {
throw new IllegalActionException(
this,
"Type of port "
+ ((CompositeEntity) getContainer())
.getEntity(actorName)
.getFullName() + "." + name
+ " should have been " + value
+ " but was " + actualValue + ".");
}
}
}
}
if (correctParameterTypes != null) {
for (Iterator actorNames = correctParameterTypes.labelSet()
.iterator(); actorNames.hasNext();) {
String actorName = (String) actorNames.next();
RecordToken assignment = (RecordToken) correctParameterTypes
.get(actorName);
for (Iterator names = assignment.labelSet().iterator(); names
.hasNext();) {
String name = (String) names.next();
StringToken value = (StringToken) assignment.get(name);
StringToken actualValue = (StringToken) ((RecordToken) actualParameterTypes
.get(actorName)).get(name);
if (!value.equals(actualValue)) {
throw new IllegalActionException(
this,
"Type of parameter "
+ ((CompositeEntity) getContainer())
.getEntity(actorName)
.getFullName() + "." + name
+ " should have been " + value
+ " but was " + actualValue + ".");
}
}
}
}
}
}
}