/* A code generator adapter that is auto generated and calls actor code. Copyright (c) 2010-2014 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.cg.kernel.generic.program.procedural.java; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import ptolemy.actor.Actor; import ptolemy.actor.IOPort; import ptolemy.actor.TypeAttribute; import ptolemy.actor.TypedAtomicActor; import ptolemy.actor.TypedCompositeActor; import ptolemy.actor.TypedIOPort; import ptolemy.actor.lib.StringConst; import ptolemy.actor.parameters.ParameterPort; import ptolemy.actor.parameters.PortParameter; import ptolemy.cg.kernel.generic.GenericCodeGenerator; import ptolemy.cg.kernel.generic.program.NamedProgramCodeGeneratorAdapter; import ptolemy.cg.kernel.generic.program.ProgramCodeGenerator; import ptolemy.cg.kernel.generic.program.TemplateParser; import ptolemy.cg.kernel.generic.program.procedural.ProceduralCodeGenerator; import ptolemy.data.BooleanToken; import ptolemy.data.IntToken; import ptolemy.data.expr.Parameter; import ptolemy.data.expr.Variable; import ptolemy.data.type.ArrayType; import ptolemy.data.type.BaseType; import ptolemy.data.type.ObjectType; import ptolemy.data.type.Type; import ptolemy.kernel.ComponentPort; import ptolemy.kernel.Entity; import ptolemy.kernel.Port; import ptolemy.kernel.Relation; import ptolemy.kernel.util.Attribute; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.Location; import ptolemy.kernel.util.NamedObj; import ptolemy.kernel.util.Settable; import ptolemy.util.StringUtilities; /////////////////////////////////////////////////////////////////// //// AutoAdapter /** * A code generator adapter that is auto generated and calls actor code. * *
This class provides a way to generate code for actors that do * not have custom code generation templates. The generated code * requires the Ptolemy kernel, actor, data and other packages.
* *This class wraps a Ptolemy actor in a TypedCompositeActor * container, makes connections from the code generated actors to the * container and invokes the actor execution methods (preinitialize(), * initialize(), prefire(), fire(), postfire() and wrapup()) of the * inner Ptolemy actor.
* *The primary entry point for this class is * {@link #getAutoAdapter(GenericCodeGenerator, Object)}
* * @author Christopher Brooks, Contributor: Edward A. Lee * @version $Id: AutoAdapter.java 70402 2014-10-23 00:52:20Z cxh $ * @since Ptolemy II 10.0 * @Pt.ProposedRating red (cxh) * @Pt.AcceptedRating red (cxh) */ public class AutoAdapter extends NamedProgramCodeGeneratorAdapter { // See // https://chess.eecs.berkeley.edu/bugzilla/show_bug.cgi?id=342 // FIXME: Rename this to AutoTypedAtomicActorAdapter? /** Construct the code generator adapter associated with the given * component. * *The primary entry point for this class is * {@link #getAutoAdapter(GenericCodeGenerator, Object)}, but this * is left public for testing.
* * @param codeGenerator The code generator with which to associate the adapter. * @param component The associated component. */ public AutoAdapter(ProgramCodeGenerator codeGenerator, TypedAtomicActor component) { super(component); TemplateParser templateParser = new JavaTemplateParser(); setTemplateParser(templateParser); templateParser.setCodeGenerator(codeGenerator); setCodeGenerator(codeGenerator); } /** * Generate the initialize code. *Generate code that creates the container, actor and ports. *
Generate code that connects the ports of the inner actor to * the ports of the outer actor. * @return The initialize code of the containing composite actor. * @exception IllegalActionException If thrown while appending to the * the block or processing the macros. */ @Override public String generateInitializeCode() throws IllegalActionException { StringBuffer code = new StringBuffer(); //String actorClassName = getComponent().getClass().getName(); //code = generateParameterCode(); String[] splitInitializeParameterCode = getCodeGenerator()._splitBody( "_AutoAdapterP_", code.toString()); // Stitch every thing together. We do this last because of // the _splitBody() calls. String resolveToplevelTypes = ""; if (_toplevelTypesResolved != getComponent().toplevel()) { // _toplevelTypesResolved is static, so we set it the the value // of the current toplevel in case we generate code for multiple // models. The Tcl test suite replicates this by generating // code for the DotProduct model multiple times. A short cut is the following: // // set args [java::new {String[]} 3 [list "-language" "java" "auto/DotProduct.xml"]] // java::call ptolemy.cg.kernel.generic.GenericCodeGenerator generateCode $args // java::call ptolemy.cg.kernel.generic.GenericCodeGenerator generateCode $args _toplevelTypesResolved = getComponent().toplevel(); resolveToplevelTypes = "try {" + _eol //+ " TypedCompositeActor.resolveTypes($containerSymbol());" + _eol + " TypedCompositeActor.resolveTypes(_toplevel);" + _eol + "} catch (Exception ex) {" + _eol + " throw new RuntimeException(\"Failed to resolve types of the top level.\", ex);" + _eol + "}" + _eol; } String splitInitializeParameterBlock = ""; if (!splitInitializeParameterCode[0].isEmpty() || !splitInitializeParameterCode[1].isEmpty()) { splitInitializeParameterBlock = "{" + _eol + splitInitializeParameterCode[0] + splitInitializeParameterCode[1] + "}" + _eol; } String result = resolveToplevelTypes + splitInitializeParameterBlock + "try {" + _eol // Initialize after the parameters are set. + " $actorSymbol(actor).initialize();" + _eol + "} catch (Exception ex) {" + _eol + " throw new RuntimeException(\"Failed to initialize $actorSymbol(actor))\", ex);" + _eol + "}" + _eol; return processCode(result); } /** Generate code for the Parameters of the actor. * @return The generated code * @exception IllegalActionException If thrown while reading the parameters * of the actor. */ public String generateParameterCode() throws IllegalActionException { StringBuffer code = new StringBuffer(); String actorClassName = getComponent().getClass().getName(); // Handle parameters. Iterator parameters = getComponent().attributeList(Settable.class) .iterator(); while (parameters.hasNext()) { Settable parameter = (Settable) parameters.next(); if (!ptolemy.actor.gui.Configurer.isVisible(getComponent(), parameter)) { continue; } String parameterName = StringUtilities.sanitizeName( parameter.getName()).replaceAll("\\$", "Dollar"); if (parameterName.equals("firingsPerIteration")) { continue; } String parameterValue = _sanitizeParameterValue(parameter); // FIXME: do we want one try block per parameter? It does // make for better error messages. boolean privateParameter = false; try { getComponent().getClass().getField(parameterName); } catch (NoSuchFieldException ex) { privateParameter = true; _needAutoAdapterSetPrivateParameter = true; // Call the method that sets the parameter. We use a method // here so as to save space. // $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/ActorWithPrivateParameterTest.xml code.append("_autoAdapterSetPrivateParameter($actorSymbol(actor), " + "\"" + parameter.getName() + "\", " + "\"" + parameterName + "\", " + "\"" + parameterValue + "\");" + _eol); } catch (SecurityException ex2) { throw new IllegalActionException(getComponent(), ex2, "Can't access " + parameterName + " field."); } if (!privateParameter) { code.append(_generateSetParameter(parameter, parameterName, actorClassName, parameterValue)); // String setParameter = ""; // if (parameter instanceof Parameter) { // setParameter = " Parameter " + parameterName + " = ((" + actorClassName + ")$actorSymbol(actor))." + parameterName + ";" + _eol // + " " + parameterName + ".setExpression(\"" // + parameterValue // + "\");" + _eol; // } else { // if (parameter instanceof ptolemy.kernel.util.StringAttribute) { // setParameter = " ptolemy.kernel.util.StringAttribute " + parameterName + " = ((" + actorClassName + ")$actorSymbol(actor))." + parameterName + ";" + _eol // + " " + parameterName + ".setExpression(\"" // + parameterValue // + "\");" + _eol; // } // } // code.append(//"try {" + _eol // "{ " + _eol // + setParameter // + " ((" + actorClassName + ")$actorSymbol(actor)).attributeChanged(" + parameterName + ");" + _eol // + "}" + _eol); } // Exclude the catch code because it bulks up the code too much for large models. // code.append("} catch (Exception ex) {" + _eol // + " throw new RuntimeException(\"Failed to set parameter \\\"" + parameterName // + "\\\" in $actorSymbol(actor) to \\\"" + StringUtilities.escapeString(parameterValue) + "\\\"\", ex);" + _eol // + "}" + _eol); } //code.append(getCodeGenerator().comment("AutoAdapter._generateInitalizeCode() start")); return code.toString(); } /** * Generate the postfire code. * @return Code that calls postfire() on the inner actor. * @exception IllegalActionException If illegal macro names are found. */ @Override public String generatePostfireCode() throws IllegalActionException { return _generateExecutionCode("postfire"); } /** * Generate the prefire code. * @return Code that calls prefire() on the inner actor. * @exception IllegalActionException If illegal macro names are found. */ @Override public String generatePrefireCode() throws IllegalActionException { return _generateExecutionCode("prefire"); } /** * Generate the preinitialize code that declares the ports. *
Generate code that declares the container, actor and ports. * @return A string of the preinitialize code for the adapter. * @exception IllegalActionException If illegal macro names are found. */ @Override public String generatePreinitializeCode() throws IllegalActionException { StringBuffer code = new StringBuffer(super.generatePreinitializeCode() + _eol + "TypedAtomicActor $actorSymbol(actor);" + _eol); if (!((BooleanToken) getCodeGenerator().variablesAsArrays.getToken()) .booleanValue()) { // Declare each container only once. NamedObj container = getComponent().getContainer(); while (container != null) { if (!_containersDeclared.contains(container)) { _containersDeclared.add(container); String containerSymbol = getCodeGenerator() .generateVariableName(container); code.append("TypedCompositeActor " + containerSymbol + ";" + _eol); } container = container.getContainer(); } } code.append(_generatePortDeclarations((Entity) getComponent())); return processCode(code.toString()); } /** Generate the preinitialization method body. * *
Typically, the preinitialize code consists of variable * declarations. However, AutoAdapter generates method calls * that instantiate wrapper TypedCompositeActors, so we need * to invoke those method calls.
* * @return a string for the preinitialization method body. In * this base class, return the empty string. * @exception IllegalActionException If there is a problem * accessing the component, its ports or the remote components. */ @Override public String generatePreinitializeMethodBodyCode() throws IllegalActionException { NamedObj component = getComponent(); String actorClassName = component.getClass().getName(); String containmentCode = _generateContainmentCode(component); String code = generatePreinitializeMethodBodyCode(component); String[] splitInitializeConnectionCode = getCodeGenerator()._splitBody( "_AutoAdapterI_", code); String toplevelSymbol = _generatePtTypedCompositeActorName( component.toplevel(), component.toplevel().getName()); // Stitch every thing together. We do this last because of // the _splitBody() calls. String result = getCodeGenerator().comment( "AutoAdapter._generatePreinitalizeCode(" + component.getFullName() + ") start") + "try {" + _eol //+ " $containerSymbol() = new TypedCompositeActor();" +_eol + " instantiateToplevel(\"" + component.toplevel().getName() + "\");" + _eol // FIXME: set this just once //+ getCodeGenerator().generateVariableName(component.toplevel()) + " = _toplevel;" + _eol + toplevelSymbol + " = _toplevel;" + _eol + containmentCode // If there are two custom actors in one container, then // we may have already created the actor. + " if ($actorSymbol(actor) == null) {" + _eol + " $actorSymbol(actor) = new " + actorClassName //+ "($containerSymbol(), \"$actorSymbol(actor)\");" + "($containerSymbol(), \"" + component.getName() + "\");" + _eol // Set the displayName so that actors that call // getDisplayName() get the same value. Actors that // generate random numbers often call getFullName(), // then should call getDisplayName() instead. + " $actorSymbol(actor).setDisplayName(\"" + component.getName() + "\");" + _eol + " }" + _eol + splitInitializeConnectionCode[0] + splitInitializeConnectionCode[1] + " if ($containerSymbol().getAttribute(\"director\") == null) {" + _eol + " new ptolemy.actor.Director($containerSymbol(), \"director\");" + _eol + " }" + _eol + "} catch (Exception ex) {" + _eol + " throw new RuntimeException(\"Failed to create $actorSymbol(actor))\", ex);" + _eol + "}" + _eol; return processCode(result); } /** Generate the preinitialization method body. * *Typically, the preinitialize code consists of variable * declarations. However, AutoAdapter generates method calls * that instantiate wrapper TypedCompositeActors, so we need * to invoke those method calls.
* * @param component The component for which the preinitialization * method is to be created. * @return a string for the preinitialization method body. In * this base class, return the empty string. * @exception IllegalActionException If there is a problem * accessing the component, its ports or the remote components. */ public String generatePreinitializeMethodBodyCode(NamedObj component) throws IllegalActionException { // Use the full class name so that we don't have to import the // actor. If we import the actor, then we cannot have model // names with the same name as the actor. //String actorClassName = component.getClass().getName(); //String containmentCode = _generateContainmentCode(component); StringBuffer code = new StringBuffer(); // Generate code that creates and connects each port. // There is very similar code in _generatePortDeclarations() Iterator entityPorts = ((Entity) component).portList().iterator(); while (entityPorts.hasNext()) { ComponentPort insidePort = (ComponentPort) entityPorts.next(); if (insidePort instanceof TypedIOPort) { TypedIOPort castPort = (TypedIOPort) insidePort; if (!castPort.isOutsideConnected()) { continue; } // True if the port has a relation whose name starts with "autoConnector". boolean hasAutoConnectorRelation = _hasAutoConnectorRelation(castPort); String name = TemplateParser.escapePortName(castPort.getName()); if (!castPort.isMultiport()) { // Only instantiate ports that are outside connected and avoid // "Cannot put a token in a full mailbox." See // $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/ActorWithPrivateParameterTest.xml code.append(_generatePortInstantiation(name, castPort.getName(), castPort, 0 /* channelNumber */, castPort.sourcePortList())); } else { // Multiports. Not all multiports have port names // that match the field name. See // $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ActorWithPortNameProblemTest.xml TypedIOPort actorPort = null; try { Field foundPortField = _findFieldByPortName(component, castPort.getName()); actorPort = (TypedIOPort) foundPortField.get(component); if (!hasAutoConnectorRelation) { code.append(" ((" + component.getClass().getName() + ")$actorSymbol(actor))." + foundPortField.getName() + ".setTypeEquals(" + _typeToBaseType(actorPort.getType()) + ");" + _eol); } } catch (Throwable throwable) { //new IllegalActionException(castPort, throwable, // "Could not find port " + castPort.getName() + " " + throwable).printStackTrace(); actorPort = (TypedIOPort) ((Entity) component) .getPort(castPort.getName()); if (!hasAutoConnectorRelation) { code.append("new TypedIOPort($containerSymbol(), \"" //+ actorPort.getName().replace("\\", "\\\\") + "\", " + AutoAdapter._externalPortName( actorPort.getContainer(), actorPort.getName()).replace("$", "\\u0024") + "\", " + actorPort.isInput() + ", " + actorPort.isOutput() + ").setMultiport(true);" + _eol); } } // If we have a multiport connected to a composite that has a multiport with two inputs, then // we want to use sourcePortList() and not numberOfSources() // $PTII/bin/ptcg -language java ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadPMultiport.xml List sourcePortList = actorPort.sourcePortList(); for (int i = 0; i < sourcePortList.size(); i++) { // If a multiport input has one channel that isAutoAdaptered and one channel that is not autoAdaptered, // then skip generating the sendInside if the channel is autoAdaptered. The test is: // $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/comm/test/auto/DeScrambler.xml if (actorPort.isOutsideConnected() && !isAutoAdaptered(((IOPort) sourcePortList .get(i)).getContainer())) { code.append(_generatePortInstantiation(name, name + "Source" + i, actorPort, i, sourcePortList)); } } List sinkPortList = actorPort.sinkPortList(); for (int i = 0; i < sinkPortList.size(); i++) { // If a multiport input has one channel that isAutoAdaptered and one channel that is not autoAdaptered, // then skip generating the sendInside if the channel is autoAdaptered. The test is: // $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/comm/test/auto/DeScrambler.xml if (actorPort.isOutsideConnected() && !isAutoAdaptered(((IOPort) sinkPortList .get(i)).getContainer())) { code.append(_generatePortInstantiation(name, name + "Sink" + i, actorPort, i, sinkPortList)); } } } ListThis method is the entry point for this class. Typically, * {@link ptolemy.cg.kernel.generic.program.procedural.java.JavaCodeGenerator#_getAutoGeneratedAdapter(GenericCodeGenerator, Object)} * calls this method.
* * @param codeGenerator The code generator with which to associate the adapter. * @param object The given object. * @return the AutoAdapter or null if object is not assignable * from TypedAtomicActor. */ public static AutoAdapter getAutoAdapter( GenericCodeGenerator codeGenerator, Object object) { // FIXME: I'm not sure if we need this method, but I like // calling something that returns null if the associated actor // cannot be found or is of the wrong type. try { Class typedAtomicActor = Class .forName("ptolemy.actor.TypedAtomicActor"); if (!typedAtomicActor.isAssignableFrom(object.getClass())) { return null; } } catch (ClassNotFoundException ex) { return null; } if (_checkingAutoAdapter) { // If the static isAutoAdaptered() method is called, then // we set _checkingAutoAdapter to true and call // GenericCodeGenerator.getAdapter(), which eventually // calls this method if the Object has no adapter. This // is a hack, but it means we don't need to modify // GenericCodeGenerator. _wouldBeAutoAdapted = true; return null; } // FIXME: I don't like casting to ProgramCodeGenerator, but we need to set // the codeGenerator of the templateParser. return new AutoAdapter((ProgramCodeGenerator) codeGenerator, (TypedAtomicActor) object); } /** Get the files needed by the code generated for this actor. * Add $(PTII) to the classpath of the generated code. * @return A set of strings that are names of the files * needed by the code generated for the Maximum actor. * @exception IllegalActionException If thrown by the superclass. */ @Override public Set getHeaderFiles() throws IllegalActionException { Set files = super.getHeaderFiles(); files.addAll(_headerFiles); files.add("ptolemy.actor.Director;"); files.add("ptolemy.actor.Manager;"); files.add("ptolemy.data.expr.Parameter;"); files.add("ptolemy.data.type.ArrayType;"); // Need IntToken etc. files.add("ptolemy.data.*;"); files.add("ptolemy.data.type.BaseType;"); files.add("ptolemy.actor.TypedAtomicActor;"); files.add("ptolemy.actor.TypedCompositeActor;"); files.add("ptolemy.actor.TypedIOPort;"); // If the actor is imported, then we cannot have models with the same // name as the actor. //files.add(getComponent().getClass().getName() + ";"); ((ProceduralCodeGenerator) getCodeGenerator()) .addLibraryIfNecessary("$(PTII)"); // Loop through the path elements in java.class.path and add // them as libraries. We need this so that we can find the // JavaScope.zip code coverage file in the nightly build ((JavaCodeGenerator) getCodeGenerator())._addClassPathLibraries(); return files; } /** * Generate shared code that includes the declaration of the toplevel * composite. If necessary a method that sets a private parameter is * added. * @exception IllegalActionException Not thrown in this base class. */ @Override public SetThis is used to put two or more custom actors in to the * same container.
* @param namedObj The NamedObj to check. * @return True if the argument would be generated using * an auto adapter. */ public boolean isAutoAdaptered(NamedObj namedObj) { return AutoAdapter.isAutoAdaptered(getCodeGenerator(), namedObj); } /** Return true if the argument would be generated using * an AutoAdapter. * *This is used to put two or more custom actors in to the * same container.
* @param codeGenerator The codegenerator. * @param namedObj The NamedObj to check. * @return True if the argument would be generated using * an auto adapter. */ public static boolean isAutoAdaptered(ProgramCodeGenerator codeGenerator, NamedObj namedObj) { try { _checkingAutoAdapter = true; _wouldBeAutoAdapted = false; try { // The adapter might be cached. Object adapter = codeGenerator.getAdapter(namedObj); if (adapter instanceof AutoAdapter) { return true; } } catch (IllegalActionException ex) { // getAdapter() will throw an exception if // getAutoAdapter() is called and _checkingAutoAdapter // is null. So, we ignore this exception } if (_wouldBeAutoAdapted) { return true; } } finally { _checkingAutoAdapter = false; _wouldBeAutoAdapted = false; } return false; } /** Return true if the port connects to a remote port that would * code generated using an AutoAdapter. * *This is used to put two or more custom actors in to the * same container.
* @param port The port to check. * @return True if the remote port would be generated using * an auto adapter. * @exception IllegalActionException If the CodeGenerator verbosity parameter * cannot be read. */ public boolean isAutoAdapteredRemotePort(Port port) throws IllegalActionException { return isAutoAdapteredRemotePort(getCodeGenerator(), port); } /** Return true if the port connects to a remote port that would * code generated using an AutoAdapter. * *This is used to put two or more custom actors in to the * same container.
* @param port The port to check. * @param codeGenerator The codegenerator. * @return True if the remote port would be generated using * an auto adapter. * @exception IllegalActionException If the CodeGenerator verbosity parameter * cannot be read. */ public static boolean isAutoAdapteredRemotePort( ProgramCodeGenerator codeGenerator, Port port) throws IllegalActionException { List linkedRelationList = port.linkedRelationList(); if (linkedRelationList.size() == 0) { return false; } Relation relation = (Relation) linkedRelationList.get(0); TypedIOPort remotePort = (TypedIOPort) relation.linkedPortList(port) .get(0); NamedObj remoteActor = remotePort.getContainer(); if (/*port.linkedRelationList().size() > 1 || */relation.linkedPortList(port).size() > 1) { // FIXME: this might be superflous, since we loop through below. boolean remoteIsAutoAdaptered = isAutoAdaptered(codeGenerator, remoteActor); StringBuffer message = new StringBuffer( "Warning: custom actors " + "connected to more than one port the same level. Msg #2\n"); Iterator relations = port.linkedRelationList().iterator(); while (relations.hasNext()) { Relation r = (Relation) relations.next(); message.append("Component: " + port.getContainer().getName() + ", port: " + port.getName() + ", relation: " + r + " is connected to:" + _eol); int i = 0; Iterator ports = r.linkedPortList(port).iterator(); while (ports.hasNext()) { Port p = (TypedIOPort) ports.next(); i++; message.append(" " + i + " " + p.getFullName() + _eol); if (!isAutoAdaptered(codeGenerator, p.getContainer())) { // If one of the remote actors is not auto // adapatered, then mark this connection as // not being autoadaptered. This is probably // a mistake, we should just handle this. // This test: // $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/test/auto/UnaryMathFunction.xml // has a bunch of custom actors that share an input relation, but the input // is a non-autoadapter actor. We would like to preserve the connectivity. message.append(" which is contained by an actor that is not an auto adapter.\n"); remoteIsAutoAdaptered = false; } } } int verbosityLevel = ((IntToken) codeGenerator.verbosity.getToken()) .intValue(); if (verbosityLevel > 0) { System.out.println("AutoAdapter: " + message); } return remoteIsAutoAdaptered; } return isAutoAdaptered(codeGenerator, remoteActor); } /////////////////////////////////////////////////////////////////// //// protected methods //// /** * Generate the fire code. *Generate code that creates tokens, sends them to the input(s) of inner
* Ptolemy actor, calls fire() on the actor and reads the outputs.
* @return The generated code.
* @exception IllegalActionException Not thrown in this base class.
*/
@Override
protected String _generateFireCode() throws IllegalActionException {
// FIXME: what if the inline parameter is set?
StringBuffer code = new StringBuffer(super._generateFireCode()
+ _eol
+ getCodeGenerator().comment(
"AutoAdapter._generateFireCode() start"));
// FIXME: it is odd that we are transferring data around in the fire code.
// Shouldn't we do this in prefire() and posfire()?
// Transfer data from the codegen variables to the actor input ports.
Iterator inputPorts = ((Actor) getComponent()).inputPortList()
.iterator();
while (inputPorts.hasNext()) {
TypedIOPort inputPort = (TypedIOPort) inputPorts.next();
String name = inputPort.getName();
Type type = inputPort.getType();
if (!isAutoAdapteredRemotePort(inputPort)) {
if (!inputPort.isMultiport()
&& inputPort.isOutsideConnected()
&& (inputPort instanceof ParameterPort || inputPort
.numLinks() > 0)) {
// Only generate code if we have a ParameterPort or we are connected.
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/AutoAdapterTwoActors.xml
code.append(_generateSendInside(name, name, type, 0));
} else {
// Multiports.
// Generate code for the sources. We don't use
// getWidth() here because IOPort.getWidth() says
// not to.
// FIXME: Shouldn't we use sourcePortList() and not numberOfSources()? See generatePreinitializeMethodBodyCode().
//int sources = inputPort.numberOfSources();
List sourcePortList = inputPort.sourcePortList();
int sources = sourcePortList.size();
//code.append(_eol + getCodeGenerator().comment("AutoAdapter._generateFireCode() MultiPort name " + name + " type: " + type + " numberOfSources: " + inputPort.numberOfSources() + " inputPort: " + inputPort + " width: " + inputPort.getWidth() + " numberOfSinks: " + inputPort.numberOfSinks()));
for (int i = 0; i < sources; i++) {
// If a multiport input has one channel that isAutoAdaptered and one channel that is not autoAdaptered,
// then skip generating the sendInside if the channel is autoAdaptered. The test is:
// $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/comm/test/auto/DeScrambler.xml
if (isAutoAdaptered(((IOPort) sourcePortList.get(i))
.getContainer())) {
continue;
}
code.append(_generateSendInside(name, name + "Source"
+ i, type, i));
if (_isReadingRemoteParameters(inputPort, i,
sourcePortList)) {
// Sigh.
// If we have a custom actor A that is
// connected to a composite that contains a
// custom actor B, but A and B are connected by
// a non-custom actor, then we need to
// transfer the token by hand. For example:
// B--> AddSubtract --> A
// Test case
// $PTII/bin/ptcg -language java ~/ptII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/knownFailedTests/ReadPMultiport2AutoD.xml
NamedObj remoteActor = ((IOPort) sourcePortList
.get(i)).getContainer();
NamedObj remoteActorContainer = remoteActor
.getContainer();
//String remoteActorContainerSymbol = getCodeGenerator().generateVariableName(remoteActorContainer);
String remoteActorContainerSymbol = _generatePtTypedCompositeActorName(
remoteActorContainer,
remoteActorContainer.getName());
// If this code is a problem, it could be
// because isReadingRemoteParameters is
// falsely returning true.
code.append("{"
+ _eol
+ "TypedCompositeActor c0 = (TypedCompositeActor) "
+ remoteActorContainerSymbol
+ ";"
+ _eol
+ "TypedIOPort c0PortA = (TypedIOPort)c0.getPort(\"c0PortA\");"
+ _eol
+ "TypedIOPort c0PortB = (TypedIOPort)c0.getPort(\"c0PortB\");"
+ _eol
+ "if ( c0PortA == null) {"
+ _eol
+ "c0PortA = new TypedIOPort(c0, \"c0PortA\", false, true);"
+ _eol
+ "} else {"
+ _eol
+ "c0PortA.setMultiport(true);"
+ _eol
+ "}"
+ _eol
+ "if ( c0PortB == null) {"
+ _eol
+ "c0PortB = new TypedIOPort(c0, \"c0PortB\", true, false);"
+ _eol
// If c0PortB does not exist, then connect it.
+ "c0.connect(c0PortB, c0PortA);"
+ _eol
+ "} else {"
+ _eol
+ "c0PortB.setMultiport(true);"
+ _eol
+ "}"
+ _eol
+ "c0PortA.setTypeEquals("
+ _typeToBaseType(inputPort.getType())
+ ");"
+ _eol
// + "if (!c0PortA.isDeeplyConnected(" + portOrParameter + ")) {" + _eol
// + " $containerSymbol().connect(" + outputPortName + ","
// + portOrParameter + ");" + _eol
// + "}" + _eol
// // Connect c0PortB if necessary. See.
// // $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadPMultiport7.xml
// // $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadPMultiport.xml
// + "if (!c0PortB.isDeeplyConnected(" + escapedCodegenPortNameSymbol + ")) {" + _eol
// + " $containerSymbol().connect("
// + escapedCodegenPortNameSymbol + ", c0PortB);" + _eol
// + "}" + _eol
+ "c0PortA.send(0, c0PortB.get(0));" + _eol
+ "}" + _eol);
}
}
// Generate code for the sinks.
int sinks = inputPort.numberOfSinks();
int width = inputPort.getWidth();
if (width < sinks) {
sinks = width;
}
// FIXME: Shouldn't we use sinkPortList() and not numberOfSinks()? See generatePreinitializeMethodBodyCode().
for (int i = 0; i < sinks; i++) {
// If a multiport input has one channel that isAutoAdaptered and one channel that is not autoAdaptered,
// then skip generating the sendInside if the channel is autoAdaptered. The test is:
// $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/comm/test/auto/DeScrambler.xml
if (isAutoAdaptered(inputPort.sinkPortList().get(i)
.getContainer())) {
continue;
}
// FIXME: it seems out that we are calling send inside on a sink?
code.append(_generateSendInside(name,
name + "Sink" + i, type, i));
}
}
}
}
// Fire the actor.
code.append("$actorSymbol(actor).fire();" + _eol);
// Transfer data from the actor output ports to the codegen variables.
Iterator outputPorts = ((Actor) getComponent()).outputPortList()
.iterator();
while (outputPorts.hasNext()) {
TypedIOPort outputPort = (TypedIOPort) outputPorts.next();
String name = outputPort.getName();
Type type = outputPort.getType();
if (!isAutoAdapteredRemotePort(outputPort)) {
// Get data from the actor.
if (!outputPort.isMultiport()) {
if (outputPort.isOutsideConnected()) {
// Place the temporary variable inside a block so that
// if we split a long body, the declaration does not
// get separated from the use.
code.append("{" + _eol
// Create temporary variables for each port so that we don't
// read from empty mailbox.
+ _generateGetInsideDeclarations(name, name,
type, 0) + _eol + "}" + _eol);
}
} else {
// Multiports.
int sources = outputPort.numberOfSources();
for (int i = 0; i < sources; i++) {
code.append("{"
+ _eol
+ _generateGetInsideDeclarations(name, name
+ "Source" + i, type, i) + _eol + "}"
+ _eol);
}
int sinks = outputPort.numberOfSinks();
for (int i = 0; i < sinks; i++) {
code.append("{"
+ _eol
+ _generateGetInsideDeclarations(name, name
+ "Sink" + i, type, i) + _eol + "}"
+ _eol);
}
}
}
}
String[] splitFireCode = getCodeGenerator()._splitBody(
"_AutoAdapterF_", code.toString());
return //"try {" + _eol
"{" + _eol + splitFireCode[0] + _eol + splitFireCode[1] + _eol + "}"
+ _eol;
//+ "} catch (Exception ex) {" + _eol
//+ " throw new RuntimeException(\"Failed to fire() $actorSymbol(actor))\", ex);" + _eol
//+ " }" + _eol;
}
///////////////////////////////////////////////////////////////////
//// private methods ////
/** Given a port name and actor name, return the name of
* the external port. This method is necessary because if there
* are two custom actors at the same level that have the same
* port name that is connected to non-custom actors, then we need
* to differentiate between them. In addition, a little magic
* concerning backslashed must occur.
* @param container The container of the port, which is almost always
* an Actor.
* @param portName The unescaped port name
* @return a combination of the port name and the actor name with
* backslashes substituted.
*/
private static String _externalPortName(NamedObj container, String portName) {
// Need to deal with backslashes in port names, see
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ActorWithPortNameProblemTest.xml
return container.getName() + "_" + portName.replace("\\", "\\\\");
}
/** Look for a field in the actor by port name. This method is
* necessary because some ports have different names than the name
* of the field.
* @param component The component that contains the port.
* Typically this parameter is calle with getComponent() as
* the first argument.
* @param portName The escaped name of the port.
* @return The field.
* @exception NoSuchFieldException If a field cannot be access, or if
* getComponent() fails.
*/
private static Field _findFieldByPortName(NamedObj component,
String portName) throws NoSuchFieldException {
portName = TemplateParser.unescapePortName(portName);
Field foundPortField = null;
// Make sure that there is a field with that name
// $PTII/ptolemy/actor/lib/string/test/auto/StringLength.xml
// has a NonStrict actor with an output that is not connected.
// If we don't check for the field, then the generated Java code
// fails.
try {
foundPortField = component.getClass().getField(portName);
} catch (NoSuchFieldException ex) {
StringBuffer portNames = new StringBuffer();
try {
// It could be that the name of the port and the variable name
// do not match.
Field[] fields = component.getClass().getFields();
for (Field field : fields) {
if (field.get(component) instanceof Port) {
Port portField = (Port) field.get(component);
String portFieldName = portField.getName();
portNames.append("<" + portFieldName + "> ");
if (portName.equals(portFieldName)) {
foundPortField = field;
break;
}
}
}
if (foundPortField == null) {
throw new NoSuchFieldException(component.getFullName()
+ "Could not find field that corresponds with "
+ portName + " Fields: " + portNames);
}
} catch (Throwable throwable2) {
throw new NoSuchFieldException(component.getFullName()
+ ": Failed to find the field that corresponds with "
+ portName + " Fields: " + portNames + ": " + ex);
}
}
return foundPortField;
}
/** Generate code that instantiates an actor. Each actor gets a method
* that is added to the set of shared methods.
* @param actor The actor
* @param actorSymbol The code generated variable name that
* refers to the actor.
* @param containerSymbol The code generated variable name that
* refers to the container of the symbol.
* @param generateContainmentCode True if the code that
* instantiates the containers should be generated. Usually,
* this parameter is true because when true, the only side
* effects of the containment code is to increase code size and
* execution time slightly.
* @param generateContainedVariables True code that instantiates
* the parameters should be generated. Usually, this false when
* actor is a NamedObj and true when actor is a
* TypedCompositeActor.
* @param generateElectricityConnections If true, invoke
* _generateElectricityConnections().
* @return code that calls the shared method that instantiates the actor
* @exception IllegalActionException If thrown while generating
* the containment code.
*/
private String _generateActorInstantiation(NamedObj actor,
String actorSymbol, String containerSymbol,
boolean generateContainmentCode,
boolean generateContainedVariables,
boolean generateElectricityConnections)
throws IllegalActionException {
//String actorSymbol = getCodeGenerator().generateVariableName(
// actor) + "_actor";
//String containerSymbol = getCodeGenerator().generateVariableName((((NamedObj) actor).getContainer()));
String code = "if ("
+ actorSymbol
+ " == null) {"
+ _eol
+ "if ("
+ containerSymbol
+ " == null) {"
+ _eol
// FIXME: we should define a method that creates the containment hierarchy.
+ (generateContainmentCode ? _generateContainmentCode(actor)
: " ")
+ "}"
+ _eol
+ actorSymbol
+ " = ("
+ actor.getClass().getName()
+ ")"
+ containerSymbol
+ ".getEntity(\""
+ actor.getName()
+ "\");"
+ _eol
+ "if ("
+ actorSymbol
+ " == null) {"
+ _eol
+ actorSymbol
+ " = new "
+ actor.getClass().getName()
+ "("
+ containerSymbol
+ ", \""
+ actor.getName()
+ "\");"
+ _eol
+ _generateStringConsts(actor, actorSymbol, containerSymbol)
+ (generateElectricityConnections ? _generateElectricityConnections(
actor, actorSymbol, containerSymbol) : "")
+ "}"
+ _eol
+ (generateContainedVariables ? _generateContainedVariables(
actor, actorSymbol) : " ")
// Set the displayName so that actors that
// call getDisplayName() get the same value.
// Actors that generate random numbers often
// call getFullName(), then should call
// getDisplayName() instead.
+ " " + actorSymbol + ".setDisplayName(\""
+ actor.getName() + "\");" + _eol + "}" + _eol + "return "
+ actorSymbol + ";" + _eol;
String nonArrayActorSymbol = "";
if (actor instanceof TypedCompositeActor) {
nonArrayActorSymbol = getCodeGenerator()
.generateVariableName(actor);
} else {
nonArrayActorSymbol = actorSymbol;
}
String methodName = "_instantiate"
+ (generateContainmentCode ? "Containment" : "")
+ (generateContainedVariables ? "Variables" : "")
+ (generateElectricityConnections ? "ECons" : "")
+ processCode(nonArrayActorSymbol);
_actorInstantiationMethods.put(methodName, processCode(code));
return methodName + "();" + _eol;
}
/** Generate code that creates the hierarchy.
* @param component The component
* @return code that creates the hierarchy
* @exception IllegalActionException If thrown while generate code that
* connects the ports of the containers.
*/
private String _generateContainmentCode(NamedObj component)
throws IllegalActionException {
StringBuffer containmentCode = new StringBuffer();
// The symbol of the container of the component, similar to $containerSymbol.
//String containerSymbol = getCodeGenerator().generateVariableName((((NamedObj) component).getContainer()));
NamedObj container = component.getContainer();
String containerSymbol = _generatePtTypedCompositeActorName(container,
container.getName());
NamedObj parentContainer = component.getContainer();
NamedObj grandparentContainer = parentContainer.getContainer();
if (grandparentContainer == null) {
// The simple case, where the actor is in the top level and
// we only need to create a TypedCompositeActor container.
// Put the actor into the toplevel so that getFullName() returns the same value.
// This is important for actors that use random numbers, as it is common to
// set the seed to seed + getFullName().hashCode().
containmentCode.append(containerSymbol + " = _toplevel;" + _eol);
} else {
// This wacky. What we do is move up the hierarchy and instantiate
// TypedComposites as necessary and *insert* the appropriate code into
// the StringBuffer. When we get to the top, we *append* code that
// inserts the hierarchy into the toplevel and that creates the container
// for the actor. At runtime, when we are generating the hierarchy,
// we need to avoid generating duplicate entities (entities that
// already exist in a container that has more than one actor handled
// by AutoAdapter).
while (parentContainer != null
&& parentContainer.getContainer() != null) {
// containmentCode.insert(0,
// "temporaryContainer = (TypedCompositeActor)cgContainer.getEntity(\""
// + parentContainer.getName()
// + "\");"
// + _eol
// + "if (temporaryContainer == null) { "
// + _eol
// + " temporaryContainer = new "
// // Use the actual class of the container, not TypedCompositeActor.
// + parentContainer.getClass().getName()
// + "(cgContainer, \""
// + parentContainer.getName() + "\");" + _eol
// + _generateStringConsts(parentContainer)
// + _generateContainedVariables(parentContainer, "temporaryContainer")
// //+ "{" + _eol
// //+ generatePreinitializeMethodBodyCode(parentContainer)
// //+ "}" + _eol
// + "}" + _eol
// + "cgContainer = temporaryContainer;" + _eol);
//String parentContainerSymbol = getCodeGenerator().generateVariableName(parentContainer);
String parentContainerSymbol = _generatePtTypedCompositeActorName(
parentContainer, parentContainer.getName());
//String parentContainerContainerSymbol = getCodeGenerator().generateVariableName(parentContainer.getContainer());
NamedObj parentContainerContainer = parentContainer
.getContainer();
String parentContainerContainerSymbol = _generatePtTypedCompositeActorName(
parentContainerContainer,
parentContainerContainer.getName());
containmentCode.insert(
0,
"cgContainer = (TypedCompositeActor)"
+ _generateActorInstantiation(parentContainer,
parentContainerSymbol,
parentContainerContainerSymbol, false,
true, true));
parentContainer = parentContainer.getContainer();
}
containmentCode.insert(
0,
"{"
+ _eol
+ getCodeGenerator().comment(
component.getFullName()) + _eol
+ "TypedCompositeActor cgContainer = _toplevel;"
+ _eol
+ "TypedCompositeActor temporaryContainer = null;"
+ _eol);
containmentCode.append(containerSymbol + " = cgContainer;" + _eol
+ "}" + _eol);
containmentCode.append(_generateContainedVariables(
component.getContainer(), containerSymbol));
// Whew.
}
return containmentCode.toString();
}
/** Instantiate Variables and locations for those actors that
* access parameters in their container.
* @param container The container in which we should look for variables and locations
* @param containerSymbol The java variable present in the generated code that refers
* to the container.
* @return code that instantiates any variables in the container.
*/
private String _generateContainedVariables(NamedObj container,
String containerSymbol) {
StringBuffer variableCode = new StringBuffer();
// Instantiate Variables for those actors that access parameters in their container.
// $PTII/bin/ptcg -language java auto/ReadParametersInContainerTest.xml
Iterator variables = container.attributeList(Variable.class).iterator();
while (variables.hasNext()) {
//if (!_importedVariable) {
// _importedVariable = true;
//}
Variable variable = (Variable) variables.next();
String variableName = StringUtilities.sanitizeName(
variable.getName()).replaceAll("\\$", "Dollar");
if (_skipVariable(variableName)) {
// No need to create _windowProperties, _vergilSize,
// _vergilZoomFactor, or _vergilCenter variables.
continue;
}
String variableClassName = variable.getClass().getName();
String variableClassShortName = variableClassName
.substring(variableClassName.lastIndexOf(".") + 1);
_headerFiles.add(variableClassName + ";");
// FIXME: optimize this by creating a method that only creates
// the variable if it is not already set. The reason we would
// have duplicate variables is because we have two custom actors
// in one container and the container has Parameters.
variableCode.append("if (" + containerSymbol + ".getAttribute(\""
+ variable.getName() + "\") == null) {" + _eol + " new "
+ variableClassShortName + "(" + containerSymbol + ", \""
+ variable.getName() + "\").setExpression(\""
+ variable.getExpression().replace("$", "\\u0024") + "\");"
+ _eol + "}" + _eol);
}
Iterator locations = container.attributeList(Location.class).iterator();
while (locations.hasNext()) {
Location location = (Location) locations.next();
String locationName = StringUtilities.sanitizeName(
location.getName()).replaceAll("\\$", "Dollar");
if (_skipVariable(locationName)) {
continue;
}
String locationClassName = location.getClass().getName();
String locationClassShortName = locationClassName
.substring(locationClassName.lastIndexOf(".") + 1);
_headerFiles.add(locationClassName + ";");
// FIXME: optimize this by creating a method that only creates
// the location if it is not already set. The reason we would
// have duplicate locations is because we have two custom actors
// in one container and the container has Parameters.
variableCode.append("if (" + containerSymbol + ".getAttribute(\""
+ location.getName() + "\") == null) {" + _eol + " new "
+ locationClassShortName + "(" + containerSymbol + ", \""
+ location.getName() + "\").setExpression(\""
+ location.getExpression() + "\");" + _eol + "}" + _eol);
}
// Include the variables of any TypedComposites that have only parameters and not actors.
// By rights, these should be ScopeExtendingAttributes . . .
if (container instanceof TypedCompositeActor) {
Iterator composites = ((TypedCompositeActor) container)
.deepCompositeEntityList().iterator();
while (composites.hasNext()) {
TypedCompositeActor composite = (TypedCompositeActor) composites
.next();
// Composites that are named "Generator" are handled specially.
// FIXME: we could have an attribute if set will generates variables.
if (composite.getName().equals("Generator")) {
variableCode
.append("{"
+ _eol
//+ "System.out.println(\"GeneratorHack: \");" + _eol
+ "TypedCompositeActor genComposite = (TypedCompositeActor)"
+ containerSymbol
+ ".getEntity(\""
+ composite.getName()
+ "\");"
+ _eol
+ "if (genComposite == null) {"
+ _eol
+ " genComposite = new "
+ composite.getClass().getName()
+ "("
+ containerSymbol
+ ", \""
+ composite.getName()
+ "\");"
+ _eol
+ _generateContainedVariables(composite,
"genComposite") + "}" + _eol + "}"
+ _eol);
}
}
}
return variableCode.toString();
}
/**
* Generate execution code for the actor execution methods.
* @param executionMethod One of "prefire", "postfire" or "wrapup".
* @return The execution code for the corresponding method.
* @exception IllegalActionException If illegal macro names are found.
*/
private String _generateExecutionCode(String executionMethod)
throws IllegalActionException {
// Syntactic sugar, avoid code duplication.
String code = "try {" + _eol + " $actorSymbol(actor)."
+ executionMethod + "();" + _eol + "} catch (Exception ex) {"
+ _eol + " throw new RuntimeException(\"Failed to "
+ executionMethod + "() $actorSymbol(actor))\", ex);" + _eol
+ "}" + _eol;
return processCode(code);
}
/**
* Return the code that gets data from the actor port and sends
* it to the codegen port
* @param actorPortName The name of the Actor port from which
* data will be read.
* @param codegenPortName The name of the port on the codegen side.
* For non-multiports, actorPortName and codegenPortName are the same.
* For multiports, codegenPortName will vary according to channel number
* while actorPortName will remain the same.
* @param type The type of the port.
* @param channel The channel number.
* For non-multiports, the channel number will be 0.
* @return The code
* @exception IllegalActionException If thrown while reading the
* variablesAsArrays parameter of the code generator.
*/
private String _generateGetInside(String actorPortName,
String codegenPortName, Type type, int channel)
throws IllegalActionException {
actorPortName = TemplateParser.escapePortName(actorPortName);
//codegenPortName = TemplateParser.escapePortName(codegenPortName);
if (type instanceof ArrayType) {
ArrayType array = (ArrayType) type;
String codeGenElementType = getCodeGenerator().codeGenType(
array.getDeclaredElementType()).replace("Integer", "Int");
String targetElementType = getCodeGenerator().targetType(
array.getDeclaredElementType());
// FIXME: do we really need a separate symbol here? This is inside a block.
//String ptolemyDataSymbol = _generatePtIOPortName(getComponent(), actorPortName + "_ptolemyData");
String ptolemyDataSymbol = "$actorSymbol(" + actorPortName
+ "_ptolemyData)";
return "{"
+ _eol
// // Get the data from the Ptolemy port
// + type.getTokenClass().getName() + " " + ptolemyData + "= (("
// + type.getTokenClass().getName() + ")($actorSymbol("
// + codegenPortName + ").getInside(0"
// // For non-multiports "". For multiports, ", 0", ", 1" etc.
// + (channel == 0 ? "" : ", " + channel)
// + ")));" + _eol
// Create an array for the codegen data.
+ _eol
+ getCodeGenerator()
.comment(
"AutoAdapter: FIXME: This will leak. We should check to see if the token already has been allocated")
+ " Token codeGenData = $Array_new("
+ "((ArrayToken)"
+ ptolemyDataSymbol
+ ").length() , 0);"
+ _eol
// Copy from the Ptolemy data to the codegen data.
+ " for (int i = 0; i < ((ArrayToken)"
+ ptolemyDataSymbol
+ ").length(); i++) {"
+ _eol
+ " Array_set(codeGenData, i, "
+ getCodeGenerator().codeGenType(
array.getDeclaredElementType()) + "_new(((("
+ codeGenElementType + "Token)(" + ptolemyDataSymbol
+ ".getElement(i)))." + targetElementType + "Value())));"
+ _eol + " }" + _eol
// Output our newly constructed token
+ " $put(" + actorPortName + ", codeGenData);" + _eol + "}"
+ _eol;
} else {
String portData = actorPortName + "_portData"
+ (channel == 0 ? "" : channel);
return "$put(" + actorPortName
// Refer to the token by the full class name and obviate the
// need to manage imports.
//+ ", (" + _generatePtIOPortName(getComponent(), portData) + "))";
+ ", ($actorSymbol(" + portData + ")))";
}
}
/**
* Return the code that creates temporary variables that hold the
* values to be read. We need to do this so as to avoid
* reading from the same Ptolemy receiver twice, which would happen
* if we have an automatically generated actor with a regular
* non-multiport that feeds its output to two actors.
* @param actorPortName The name of the Actor port from which
* data will be read.
* @param codegenPortName The name of the port on the codegen side.
* For non-multiports, actorPortName and codegenPortName are the same.
* For multiports, codegenPortName will vary according to channel number
* while actorPortName will remain the same.
* @param type The type of the port.
* @param channel The channel number.
* For non-multiports, the channel number will be 0.
* @return The inside declarations
* @exception IllegalActionException If thrown while generating code to to
* gets data from the actor port and sends it to the codegen port.
*/
private String _generateGetInsideDeclarations(String actorPortName,
String codegenPortName, Type type, int channel)
throws IllegalActionException {
actorPortName = TemplateParser.escapePortName(actorPortName);
codegenPortName = TemplateParser.escapePortName(codegenPortName);
String codegenPortNameSymbol = _generatePtIOPortName(getComponent(),
codegenPortName);
// This method is needed by $PTII/ptolemy/actor/lib/comm/test/auto/DeScrambler.xml
String portData = actorPortName + "_portData"
+ (channel == 0 ? "" : channel);
if (type instanceof ArrayType) {
ArrayType array = (ArrayType) type;
String codeGenElementType = getCodeGenerator().codeGenType(
array.getDeclaredElementType()).replace("Integer", "Int");
String targetElementType = getCodeGenerator().targetType(
array.getDeclaredElementType());
// FIXME: do we really need a separate symbol here? This is inside a block.
//String ptolemyDataSymbol = _generatePtIOPortName(getComponent(), actorPortName + "_ptolemyData");
String ptolemyDataSymbol = "$actorSymbol(" + actorPortName
+ "_ptolemyData)";
return
// Get the data from the Ptolemy port
type.getTokenClass().getName()
+ " "
+ ptolemyDataSymbol
+ " = (("
+ type.getTokenClass().getName()
+ ")("
+ codegenPortNameSymbol
+ ".getInside(0"
// For non-multiports "". For multiports, ", 0", ", 1" etc.
+ (channel == 0 ? "" : ", " + channel)
+ ")));"
+ _eol
// Create an array for the codegen data.
+ _eol
+ getCodeGenerator()
.comment(
"AutoAdapter: FIXME: This will leak. We should check to see if the token already has been allocated")
+ " Token "
+ " $actorSymbol("
+ portData
+ ")"
+ " = $Array_new("
+ "((ArrayToken)"
+ ptolemyDataSymbol
+ ").length(), 0);"
+ _eol
// Copy from the Ptolemy data to the codegen data.
+ " for (int i = 0; i < ((ArrayToken)"
+ ptolemyDataSymbol
+ ").length(); i++) {"
+ _eol
+ " Array_set("
+ "$actorSymbol("
+ portData
+ ")"
+ ", i, "
+ getCodeGenerator().codeGenType(
array.getDeclaredElementType())
+ "_new(((("
+ codeGenElementType
+ "Token)("
+ ptolemyDataSymbol
+ ".getElement(i)))."
+ targetElementType
+ "Value())));"
+ _eol
+ " }"
+ _eol
+ _generateGetInside(actorPortName, codegenPortName, type,
channel);
} else if (type == BaseType.COMPLEX) {
_headerFiles.add("ptolemy.math.Complex;");
return "$targetType("
+ actorPortName
+ ") $actorSymbol("
+ portData
+ ");"
+ _eol
+ "Complex complex = (Complex)((("
+ type.getTokenClass().getName()
+ ")"
+ "("
+ codegenPortNameSymbol
+ ".getInside(0"
+ ")))."
+ _valueMethodName(type)
+ ");"
+ _eol
+ "double real = complex.real;"
+ _eol
+ "double imag = complex.imag;"
+ _eol
+ "$actorSymbol("
+ portData
+ ")"
+ " = $typeFunc(TYPE_Complex::new(real, imag));"
+ _eol
+ _generateGetInside(actorPortName, codegenPortName, type,
channel);
// For non-multiports "". For multiports, ", 0", ", 1" etc.
//+ (channel == 0 ? "" : ", " + channel)
} else if (type.equals(BaseType.OBJECT)) {
//_headerFiles.add("ptolemy.math.Complex;");
return "$targetType("
+ actorPortName
+ ") $actorSymbol("
+ portData
+ ");"
+ _eol
+ "Object object = (Object)((("
+ type.getTokenClass().getName()
+ ")"
+ "("
+ codegenPortNameSymbol
+ ".getInside(0"
+ ")))."
+ _valueMethodName(type)
+ ");"
+ _eol
+ "$actorSymbol("
+ portData
+ ")"
+ " = $typeFunc(TYPE_Object::new(object));"
+ _eol
+ _generateGetInside(actorPortName, codegenPortName, type,
channel);
// return "$targetType("
// + actorPortName
// + ") $actorSymbol("
// + portData
// + ");"
// + _eol
// + "ObjectToken objectToken = (ObjectToken)"
// + codegenPortNameSymbol
// + ".getInside(0);" + _eol
// + "$actorSymbol(" + portData + ") = objectToken.getValue();"
// + _eol
// + _generateGetInside(actorPortName, codegenPortName, type,
// channel);
// For non-multiports "". For multiports, ", 0", ", 1" etc.
//+ (channel == 0 ? "" : ", " + channel)
} else {
return "// type: "
+ type
+ "\n$targetType("
+ actorPortName
+ ") $actorSymbol("
+ portData
+ ");"
+ _eol
+ "if ("
+ codegenPortNameSymbol
+ ".hasTokenInside(0)) {"
+ _eol
+ " $actorSymbol("
+ portData
+ ") = "
+ "(("
+ type.getTokenClass().getName()
+ ")"
+ "("
+ codegenPortNameSymbol
+ ".getInside(0"
+ ")))."
+ _valueMethodName(type)
+ ";"
+ _eol
+ _generateGetInside(actorPortName, codegenPortName, type,
channel) + _eol + "}" + _eol;
// For non-multiports "". For multiports, ", 0", ", 1" etc.
//+ (channel == 0 ? "" : ", " + channel)
}
}
/** Return the code that is used to connect ports for situations
* where an actor may read parameters from a remote container.
* @param readingReoteParametersDepth The depth at which remote
* parameters are being read.
* @param portOrParameter The port or parameter.
* @param escapedCodegenPortName The escaped port name.
* @return The code
* @exception IllegalActionException If thrown while reading the
* variablesAsArrays parameter of the code generator.
*/
private String _generateRemoteParameterConnections(
int readingRemoteParametersDepth, String portOrParameter,
String escapedCodegenPortName) throws IllegalActionException {
String outputPortName = "c" + readingRemoteParametersDepth + "PortA";
String escapedCodegenPortNameSymbol = _generatePtIOPortName(
getComponent(), escapedCodegenPortName);
return "if (!c0PortA.isDeeplyConnected(" + portOrParameter + ")) {"
+ _eol + " $containerSymbol().connect(" + outputPortName
+ "," + portOrParameter + ");" + _eol
+ " $containerSymbol().connect("
+ escapedCodegenPortNameSymbol + ", c0PortB);" + _eol + "}"
+ _eol;
}
/** Generate port declaration code for a composite or atomic entity.
* @return the port declaration code.
* @exception IllegalActionException If the variablesAsArrays parameter
* of the code generator cannot be read.
*/
private StringBuffer _generatePortDeclarations(Entity entity)
throws IllegalActionException {
StringBuffer code = new StringBuffer();
// Handle inputs and outputs on a per-actor basis.
// There is very similar code in generatePreinitializeMethodBodyCode()
Iterator entityPorts = entity.portList().iterator();
while (entityPorts.hasNext()) {
ComponentPort insidePort = (ComponentPort) entityPorts.next();
if (insidePort instanceof TypedIOPort) {
TypedIOPort castPort = (TypedIOPort) insidePort;
if (!castPort.isOutsideConnected()) {
continue;
}
String name = TemplateParser.escapePortName(castPort.getName());
if (!castPort.isMultiport()) {
if (!((BooleanToken) getCodeGenerator().variablesAsArrays
.getToken()).booleanValue()) {
code.append("TypedIOPort $actorSymbol(" + name + ");"
+ _eol);
}
} else {
// FIXME: We instantiate a separate external port for each channel
// of the multiport. Could we just connect directly to the channels
// of the multiport? The problem I had was that the receivers are
// not created if I connect directly to the channels.
// Use castPort.getName() and get the real name of the port.
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ActorWithPortNameProblemTest.xml
IOPort actorPort = (IOPort) ((Entity) getComponent())
.getPort(castPort.getName());
if (actorPort == null) {
Entity container = (Entity) getComponent()
.getContainer();
if (container != null) {
// Needed by
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadParamIC2.xml
actorPort = (IOPort) container.getPort(castPort
.getName());
}
if (actorPort == null) {
System.out.println(getComponent().getContainer()
.exportMoML());
throw new NullPointerException(
"Could not find port \""
+ castPort.getName()
+ "\" in "
+ getComponent().getFullName()
+ (container != null ? " or "
+ container.getFullName()
: ""));
}
}
List sourcePortList = castPort.sourcePortList();
for (int i = 0; i < sourcePortList.size(); i++) {
if (!((BooleanToken) getCodeGenerator().variablesAsArrays
.getToken()).booleanValue()) {
code.append("TypedIOPort $actorSymbol(" + name
+ "Source" + i + ");" + _eol);
}
// True if the port is an input multiport connected to an
// actor in a container that has parameters.
if (_isReadingRemoteParameters(castPort, i,
sourcePortList)) {
NamedObj remoteActorContainer = ((IOPort) sourcePortList
.get(i)).getContainer().getContainer();
if (remoteActorContainer != null
&& !((BooleanToken) getCodeGenerator().variablesAsArrays
.getToken()).booleanValue()) {
if (!_containersDeclared
.contains(remoteActorContainer)) {
_containersDeclared
.add(remoteActorContainer);
//String remoteActorContainerSymbol = getCodeGenerator().generateVariableName(remoteActorContainer);
String remoteActorContainerSymbol = _generatePtTypedCompositeActorName(
remoteActorContainer,
remoteActorContainer.getName());
code.append("TypedCompositeActor "
+ remoteActorContainerSymbol + ";"
+ _eol);
}
NamedObj remoteActorContainerContainer = ((IOPort) sourcePortList
.get(i)).getContainer().getContainer()
.getContainer();
if (remoteActorContainerContainer != null
&& !_containersDeclared
.contains(remoteActorContainerContainer)) {
_containersDeclared
.add(remoteActorContainerContainer);
//String remoteActorContainerContainerSymbol = getCodeGenerator().generateVariableName(remoteActorContainerContainer);
String remoteActorContainerContainerSymbol = _generatePtTypedCompositeActorName(
remoteActorContainerContainer,
remoteActorContainerContainer
.getName());
code.append("TypedCompositeActor "
+ remoteActorContainerContainerSymbol
+ ";" + _eol);
}
}
}
}
if (!((BooleanToken) getCodeGenerator().variablesAsArrays
.getToken()).booleanValue()) {
List sinkPortList = castPort.sinkPortList();
for (int i = 0; i < sinkPortList.size(); i++) {
code.append("TypedIOPort $actorSymbol(" + name
+ "Sink" + i + ");" + _eol);
}
}
}
}
}
return code;
}
/** Return the code necessary to instantiate the port.
* @param actorPortName The escaped name of the Actor port to be instantiated.
* @param codegenPortName The name of the port on the codegen side.
* For non-multiports, actorPortName and codegenPortName are the same.
* For multiports, codegenPortName will vary according to channel number
* while actorPortName will remain the same.
* @param port The port of the actor.
* @param channelNumber The number of the channel. For
* singlePorts, the channelNumber will be 0. For multiports, the
* channelNumber will range from 0 to the number of sinks or
* sources.
* @param sourceOrSinkPorts The source or sink ports connected to the port.
* @exception IllegalActionException If there is a problem checking whether
* actorPortName is a PortParameter.
*/
private String _generatePortInstantiation(String actorPortName,
String codegenPortName, TypedIOPort port, int channelNumber,
List sourceOrSinkPorts) throws IllegalActionException {
//String escapedActorPortName = TemplateParser.escapePortName(actorPortName);
String unescapedActorPortName = TemplateParser
.unescapePortName(actorPortName);
String escapedCodegenPortName = TemplateParser
.escapePortName(codegenPortName);
String escapedCodegenPortNameSymbol = _generatePtIOPortName(
getComponent(), escapedCodegenPortName);
PortParameter portParameter = (PortParameter) getComponent()
.getAttribute(actorPortName, PortParameter.class);
// Multiport need to have different codegenPortNames, see
// $PTII/bin/ptcg -language java $PTII/ptolemy/actor/lib/test/auto/Gaussian1.xml
// There are some custom actors that reach across their links and read
// parameters from the actor on the other side:
//
// If we have a model CompositeA -> ActorB
// has a parameter named "remoteParameter" and ActorB is a
// ptolemy/cg/kernel/generic/program/procedural/java/test/ReadParametersAcrossLink.java
// then that actor reads the value of the "remoteParameter"
// parameter in CompositeA.
//
// To test this:
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/ReadPsIC.xml
// First, determine if the port is an input port that is connected
// to a TypedComposite that has parameters. If it is, then generate
// a composite that contains the parameters and connect our code generator
// to its input. If we CompositeA -> ActorB, then we generate CompositeC
// and generate code that creates CompositeC -> ActorB.
// True if the port is an input multiport connected to an
// actor in a container that has parameters.
boolean readingRemoteParameters = _isReadingRemoteParameters(port,
channelNumber, sourceOrSinkPorts);
int verbosityLevel = ((IntToken) getCodeGenerator().verbosity
.getToken()).intValue();
StringBuffer code = new StringBuffer("{" + _eol);
int readingRemoteParametersDepth = 0;
if (readingRemoteParameters) {
NamedObj remoteActor = ((IOPort) sourceOrSinkPorts
.get(channelNumber)).getContainer();
// Check to see if the container of the container of the
// remote actor is null. Needed for:
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/PortWithStarsInName2.xml
if (remoteActor.getContainer().getContainer() == null) {
if (verbosityLevel > 4) {
System.out
.println(getComponent().getFullName()
+ " remote actor "
+ remoteActor.getFullName()
+ " is close to the top, setting readingRemoteParameters to false");
}
readingRemoteParameters = false;
} else {
//String remoteActorSymbol = getCodeGenerator().generateVariableName(
// remoteActor) + "_actor";
//String remoteActorContainerSymbol = getCodeGenerator().generateVariableName((((NamedObj) remoteActor).getContainer()));
NamedObj remoteActorContainer = remoteActor.getContainer();
String remoteActorContainerSymbol = _generatePtTypedCompositeActorName(
remoteActorContainer, remoteActorContainer.getName());
// FIXME: remoteActor.getContainer().getContainer() be null?
//String remoteActorContainerContainerSymbol = getCodeGenerator().generateVariableName((((NamedObj) remoteActor).getContainer().getContainer()));
NamedObj remoteActorContainerContainer = remoteActorContainer
.getContainer();
String remoteActorContainerContainerSymbol = _generatePtTypedCompositeActorName(
remoteActorContainerContainer,
remoteActorContainerContainer.getName());
if (verbosityLevel > 4) {
System.out
.println(getComponent().getFullName()
+ " remote actor "
+ remoteActor.getFullName()
+ "Setting readingRemoteParameters "
+ (remoteActor.getContainer()
.getContainer() != port
.getContainer().getContainer())
+ " "
+ remoteActor.getContainer().getContainer()
.getContainer() != null);
}
if (remoteActor.getContainer().getContainer() != port
.getContainer().getContainer()
&& remoteActor.getContainer().getContainer()
.getContainer() != null) {
// The remoteActor could be contained by a composite, so we create connections
// all the way up.
// FIXME: In theory, we should have a loop here to deal with arbitrary depth
readingRemoteParametersDepth = 1;
if (verbosityLevel > 4) {
System.out.println(getComponent().getFullName()
+ " remote actor " + remoteActor.getFullName()
+ "Setting readingRemoteParametersDepth "
+ readingRemoteParametersDepth);
}
//String remoteActorC3Symbol = getCodeGenerator().generateVariableName((((NamedObj) remoteActor).getContainer().getContainer().getContainer()));
NamedObj remoteActorC3 = remoteActor.getContainer()
.getContainer().getContainer();
String remoteActorC3Symbol = _generatePtTypedCompositeActorName(
remoteActorC3, remoteActorC3.getName());
code.append("TypedCompositeActor c1 = (TypedCompositeActor)"
+ _generateActorInstantiation(remoteActor
.getContainer().getContainer(),
remoteActorContainerContainerSymbol,
remoteActorC3Symbol, true, true, true));
code.append("TypedIOPort c1PortA = (TypedIOPort)c1.getPort(\"c1PortA\");"
+ _eol
+ "if ( c1PortA == null) {"
+ _eol
+ "c1PortA = new TypedIOPort(c1, \"c1PortA\", false, true);"
+ _eol
+ "}"
+ _eol
+ "TypedIOPort c1PortB = (TypedIOPort)c1.getPort(\"c1PortB\");"
+ _eol
+ "if ( c1PortB == null) {"
+ _eol
+ "c1PortB = new TypedIOPort(c1, \"c1PortB\", true, false);"
+ _eol
// If c1PortB does not exist, then connect it.
//+ "c1.connect(c1PortB, c1PortA);" + _eol
+ "}" + _eol);
}
code.append("TypedCompositeActor c0 = (TypedCompositeActor)"
+ _generateActorInstantiation(
remoteActor.getContainer(),
remoteActorContainerSymbol,
remoteActorContainerContainerSymbol, true,
true, true));
// Create the input and output ports and connect them.
if (verbosityLevel > 3) {
code.append(" System.out.println(\"E1 " + actorPortName
+ " " + port.getFullName() + " " + channelNumber
+ " " + remoteActor.getFullName() + "\");" + _eol);
}
// We set the port as a multiport if necessary, see:
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadPMultiport7.xml
// $PTII/bin/ptcg -language java $PTII/ptolemy/cg/kernel/generic/program/procedural/java/test/auto/ReadPMultiport.xml
code.append("TypedIOPort c0PortA = (TypedIOPort)c0.getPort(\"c0PortA\");"
+ _eol
+ "if ( c0PortA == null) {"
+ _eol
+ "c0PortA = new TypedIOPort(c0, \"c0PortA\", false, true);"
+ _eol
+ "} else {"
+ _eol
+ "c0PortA.setMultiport(true);"
+ _eol
+ "}"
+ _eol
+ "TypedIOPort c0PortB = (TypedIOPort)c0.getPort(\"c0PortB\");"
+ _eol
+ "if ( c0PortB == null) {"
+ _eol
+ "c0PortB = new TypedIOPort(c0, \"c0PortB\", true, false);"
+ _eol
// If c0PortB does not exist, then connect it.
+ "c0.connect(c0PortB, c0PortA);"
+ _eol
+ "} else {"
+ _eol
+ "c0PortB.setMultiport(true);"
+ _eol
+ "}"
+ _eol);
}
}
// If the remote port belongs to an actor that would
// use an AutoAdapter (that is, an actor for which there
// is no template), then connect directly to the remote port.
// This allows us to put multiple custom actors in one composite.
// remoteIsAutoAdaptered is true if the remote actor is a
// custom actor. The idea is that the layout of the custom
// actors in the code generated MoML fragment should match the
// original layout. Custom actors may communicate with each
// other via backdoor methods, so we keep custom actors in the
// same container.
// We don't call isAutoAdapteredRemotePort() here because we
// want to use these variables anyway.
boolean remoteIsAutoAdaptered = false;
Set