/* A TypedCompositeActor with Lazy evaluation for Modular code generation.
Copyright (c) 2009-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.lib;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import ptolemy.actor.Actor;
import ptolemy.actor.CompositeActor;
import ptolemy.actor.Director;
import ptolemy.actor.IOPort;
import ptolemy.actor.IORelation;
import ptolemy.actor.TypedIOPort;
import ptolemy.actor.TypedIORelation;
import ptolemy.actor.util.DFUtilities;
import ptolemy.cg.kernel.generic.CodeGeneratorAdapter;
import ptolemy.cg.kernel.generic.program.procedural.java.JavaCodeGenerator;
import ptolemy.cg.kernel.generic.program.procedural.java.modular.ModularSDFCodeGenerator;
import ptolemy.cg.lib.Profile.FiringFunction;
import ptolemy.data.BooleanToken;
import ptolemy.data.expr.FileParameter;
import ptolemy.data.expr.Parameter;
import ptolemy.domains.sdf.kernel.SDFDirector;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.InternalErrorException;
import ptolemy.kernel.util.KernelException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.Nameable;
import ptolemy.kernel.util.NamedObj;
import ptolemy.kernel.util.Workspace;
///////////////////////////////////////////////////////////////////
////ModularCodeGenTypedCompositeActor
/**
An aggregation of typed actors with lazy evaluation. The contents of
this actor can be created in the usual way via visual editor by dragging
in other actors and ports and connecting them. When it exports a MoML
description of itself, it describes its ports and parameters in the
usual way, but contained actors, relations, and their interconnections
are exported within <configure> </configure> tags.
When reloading the MoML description, evaluation of the MoML
within the configure tags is deferred until there is an explicit
request for the contents. This behavior is useful for large
complicated models where the time it takes to instantiate the
entire model is large. It permits opening and browsing the model
without a long wait. However, the cost comes typically when
running the model. The instantiation time will be added to
the time it takes to preinitialize the model.
The lazy contents of this composite are specified via the configure()
method, which is called by the MoML parser and passed MoML code.
The MoML is evaluated lazily; i.e. it is not actually evaluated
until there is a request for its contents, via a call to
getEntity(), numEntities(), entityList(), relationList(),
or any related method. You can also force evaluation
of the MoML by calling populate(). Accessing the attributes
or ports of this composite does not trigger a populate() call,
so a visual editor can interact with the actor from the outside
in the usual way, enabling connections to its ports, editing
of its parameters, and rendering of its custom icon, if any.
The configure method can be passed a URL or MoML text or both.
If it is given MoML text, that text will normally be wrapped in a
processing instruction, as follows:
The processing instruction, which is enclosed in "<?" and "?>"
prevents premature evaluation of the MoML. The processing instruction
has a target, "moml", which specifies that it contains MoML code.
The keyword "moml" in the processing instruction must
be exactly as above, or the entire processing instruction will
be ignored. The populate() method
strips off the processing instruction and evaluates the MoML elements.
The group element allows the library contents to be given as a set
of elements (the MoML parser requires that there always be a single
top-level element, which in this case will be the group element).
One subtlety in using this class arises because of a problem typical
of lazy evaluation. A number of exceptions may be thrown because of
errors in the MoML code when the MoML code is evaluated. However,
since that code is evaluated lazily, it is evaluated in a context
where these exceptions are not expected. There is no completely
clean solution to this problem; our solution is to translate all
exceptions to runtime exceptions in the populate() method.
This method, therefore, violates the condition for using runtime
exceptions in that the condition that causes these exceptions to
be thrown is not a testable precondition.
A second subtlety involves cloning. When this class is cloned,
if the configure MoML text has not yet been evaluated, then the clone
is created with the same (unevaluated) MoML text, rather than being
populated with the contents specified by that text. If the object
is cloned after being populated, the clone will also be populated.
Cloning is used in actor-oriented classes to create subclasses
or instances of a class. When a LazyTypedCompositeActor contained
by a subclass or an instance is populated, it delegates to the
instance in the class definition. When that instance is populated,
all of the derived instances in subclasses and instances of the
class will also be populated as a side effect.
A third subtlety is that parameters of this actor cannot refer to
contained entities or relations, nor to attributes contained by
those. This is a rather esoteric use of expressions, so
this limitation may not be onerous. You probably didn't know
you could do that anyway. An attempt to make such references
will simply result in the expression failing to evaluate.
@author Dai Bui
@version $Id: ModularCompiledSDFTypedCompositeActor.java 70402 2014-10-23 00:52:20Z cxh $
@since Ptolemy II 10.0
@Pt.ProposedRating Red (rodiers)
@Pt.AcceptedRating Red (rodiers)
*/
public class ModularCompiledSDFTypedCompositeActor extends
ModularCodeGenLazyTypedCompositeActor {
/** Construct a library in the default workspace with no
* container and an empty string as its name. Add the library to the
* workspace directory.
* Increment the version number of the workspace.
* @exception IllegalActionException If the entity cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public ModularCompiledSDFTypedCompositeActor()
throws IllegalActionException, NameDuplicationException {
super();
_init();
}
/** Construct a library in the specified workspace with
* no container and an empty string as a name. You can then change
* the name with setName(). If the workspace argument is null, then
* use the default workspace. Add the actor to the workspace directory.
* Increment the version number of the workspace.
* @param workspace The workspace that will list the actor.
* @exception IllegalActionException If the entity cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public ModularCompiledSDFTypedCompositeActor(Workspace workspace)
throws IllegalActionException, NameDuplicationException {
super(workspace);
_init();
}
/** Construct a library with the given container and name.
* @param container The container.
* @param name The name of this library.
* @exception IllegalActionException If the entity cannot be contained
* by the proposed container.
* @exception NameDuplicationException If the container already has an
* actor with this name.
*/
public ModularCompiledSDFTypedCompositeActor(CompositeEntity container,
String name) throws NameDuplicationException,
IllegalActionException {
super(container, name);
_init();
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** React to a change in an attribute. This method is called by
* a contained attribute when its value changes. This overrides
* the base class so that if the attribute is an instance of
* TypeAttribute, then it sets the type of the port.
* @param attribute The attribute that changed.
* @exception IllegalActionException If the change is not acceptable
* to this container.
*/
@Override
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
super.attributeChanged(attribute);
if (attribute == recompileHierarchy) {
// We will set the recompileHierarchy of all directly contained
// ModularCodeGenTypedCompositeActors.
// These will then do the same.
if (((BooleanToken) recompileHierarchy.getToken()).booleanValue()) {
List> entities = entityList(ModularCompiledSDFTypedCompositeActor.class);
for (Object entity : entities) {
((ModularCompiledSDFTypedCompositeActor) entity).recompileHierarchy
.setToken(new BooleanToken(true));
}
}
} else if (attribute != recompileThisLevel) {
// We don't support this yet. Enabling results in a recompilation when
// opening the model since expressions are lazy, and the notification does
// not happen when you parse the model, but when you read the model.
//_setRecompileFlag();
} else {
super.attributeChanged(attribute);
}
}
/** Create receivers for each port. If the port is an
* input port, then receivers are created for outside
* connections. If it is an output port, then receivers
* are created for inside connections. This method replaces
* any pre-existing receivers, so any data they contain
* will be lost.
* @exception IllegalActionException If any port throws it.
*/
@Override
public void createReceivers() throws IllegalActionException {
if (_modelChanged()) {
super.createReceivers();
} else {
if (workspace().getVersion() != _receiversVersion) {
Iterator> ports = portList().iterator();
try {
workspace().getWriteAccess();
while (ports.hasNext()) {
IOPort onePort = (IOPort) ports.next();
if (onePort.isInput()) {
onePort.createReceivers();
}
}
_receiversVersion = workspace().getVersion();
} finally {
// Note that this does not increment the workspace version.
// We have not changed the structure of the model.
workspace().doneTemporaryWriting();
}
}
}
}
/** Get the ports belonging to this entity.
* The order is the order in which they became contained by this entity.
* This method is read-synchronized on the workspace.
* @return An unmodifiable list of Port objects.
*/
@Override
public List portList() {
Profile profile = getProfile();
if (_USE_PROFILE && profile != null) {
List ports = new LinkedList(
super.portList());
HashSet portSet = new HashSet();
for (Object port : ports) {
portSet.add(((NamedObj) port).getName());
}
try {
for (Profile.Port port : profile.ports()) {
if (port.publisher()) {
if (!portSet.contains(port.name())) {
IOPort newPort = new TypedIOPort(this, port.name());
new Parameter(newPort, "_hide", BooleanToken.TRUE);
newPort.setInput(port.input());
newPort.setOutput(port.output());
ports.add(new TypedIOPort(this, port.name()));
NamedObj container = getContainer();
if (container instanceof CompositeActor) {
((CompositeActor) container)
.registerPublisherPort(
port.getPubSubChannelName(),
newPort);
}
}
}
}
return ports;
} catch (IllegalActionException e) {
profile = null;
} catch (NameDuplicationException e) {
profile = null;
}
}
if (!_USE_PROFILE || profile == null) {
populate();
List> entities = entityList(ModularCompiledSDFTypedCompositeActor.class);
for (Object entity : entities) {
((ModularCompiledSDFTypedCompositeActor) entity).populate();
}
} else {
System.err.println("Error");
}
return super.portList();
}
/** If this actor is opaque, transfer any data from the input ports
* of this composite to the ports connected on the inside, and then
* invoke the fire() method of its local director.
* The transfer is accomplished by calling the transferInputs() method
* of the local director (the exact behavior of which depends on the
* domain). If the actor is not opaque, throw an exception.
* This method is read-synchronized on the workspace, so the
* fire() method of the director need not be (assuming it is only
* called from here). After the fire() method of the director returns,
* send any output data created by calling the local director's
* transferOutputs method.
*
* @exception IllegalActionException If there is no director, or if
* the director's fire() method throws it, or if the actor is not
* opaque.
*/
@Override
public void fire() throws IllegalActionException {
if (_fireMethod == null) {
if (_debugging) {
_debug("ModularCodeGenerator: No generated code. Calling simulation fire method.");
}
System.out
.println("ModularCodeGenerator: No generated code. Calling simulation fire method.");
super.fire();
return;
}
try {
// Invoke the native fire method
if (_debugging) {
_debug("ModularCodeGenerator: Calling fire method for generated code.");
}
List