/* Causality interface for FSM actors.
Copyright (c) 2003-2009 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.domains.fsm.modal;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ptolemy.actor.Actor;
import ptolemy.actor.IOPort;
import ptolemy.actor.util.CausalityInterface;
import ptolemy.actor.util.CausalityInterfaceForComposites;
import ptolemy.actor.util.Dependency;
import ptolemy.kernel.Entity;
import ptolemy.kernel.Port;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NamedObj;
//////////////////////////////////////////////////////////////////////////
//// MirrorCausalityInterface
/**
This class infers a causality interface from causality interfaces provided
in the constructor and in the {@link #composeWith} method. For each of these
interfaces, this interface finds ports in its own actor that match the names
of those for the specified interfaces, and constructs dependencies that
are oPlus compositions of the dependencies in the specified interfaces for
ports with the same names. For equivalence classes, it merges the equivalence
classes so that if two ports are equivalent in any of the provided causality
interfaces, then the corresponding (same named) ports in the actor are also
equivalent.
@author Edward A. Lee
@version $Id$
@since Ptolemy II 7.1
@Pt.ProposedRating Yellow (eal)
@Pt.AcceptedRating Red (eal)
*/
public class MirrorCausalityInterface extends CausalityInterfaceForComposites {
/** Construct a causality interface that mirrors the specified causality
* interface.
* @param actor The actor for which this is an interface.
* @param causality The interface to mirror.
* @exception IllegalArgumentException If the actor parameter is not
* an instance of CompositeEntity.
*/
public MirrorCausalityInterface(Actor actor, CausalityInterface causality)
throws IllegalArgumentException {
super(actor, causality.getDefaultDependency());
_composedInterfaces.add(causality);
}
///////////////////////////////////////////////////////////////////
//// public methods ////
/** Add the specified causality interface for the specified actor.
* @param causality The interface to compose with the one specified
* in the constructor.
*/
public void composeWith(CausalityInterface causality) {
_composedInterfaces.add(causality);
}
/** Return the dependency between the specified input port
* and the specified output port. This is done by checking
* the guards and actions of all the transitions.
* When called for the first time since a change in the model
* structure, this method performs the complete analysis of
* the FSM and caches the result. Subsequent calls just
* look up the result.
* @param input The input port.
* @param output The output port, or null to update the
* dependencies (and record equivalence classes) without
* requiring there to be an output port.
* @return The dependency between the specified input port
* and the specified output port, or null if a null output
* is port specified.
* @exception IllegalActionException If a guard expression cannot be parsed.
*/
public Dependency getDependency(IOPort input, IOPort output)
throws IllegalActionException {
// If the dependency is not up-to-date, then update it.
long workspaceVersion = ((NamedObj) _actor).workspace().getVersion();
if (_dependencyVersion != workspaceVersion) {
// Need to update dependencies. The cached version
// is obsolete.
try {
((NamedObj) _actor).workspace().getReadAccess();
_reverseDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
_forwardDependencies = new HashMap<IOPort, Map<IOPort, Dependency>>();
// Iterate over all the associated interfaces.
for (CausalityInterface causality : _composedInterfaces) {
List<IOPort> mirrorInputs = causality.getActor()
.inputPortList();
for (IOPort mirrorInput : mirrorInputs) {
Port localInput = ((Entity) _actor).getPort(mirrorInput
.getName());
if (!(localInput instanceof IOPort)) {
throw new IllegalActionException(_actor,
mirrorInput.getContainer(),
"No matching port with name "
+ mirrorInput.getName());
}
// The localInput may not be an input port...
// It may have been an output port that the FSMActor controller
// also has as an input port. But we don't want to set a forward
// depedency in this case.
if (!((IOPort) localInput).isInput()) {
continue;
}
Map<IOPort, Dependency> forwardMap = _forwardDependencies
.get(localInput);
if (forwardMap == null) {
forwardMap = new HashMap<IOPort, Dependency>();
_forwardDependencies.put((IOPort) localInput,
forwardMap);
}
for (IOPort dependentOutput : causality
.dependentPorts(mirrorInput)) {
Port localOutput = ((Entity) _actor)
.getPort(dependentOutput.getName());
if (!(localOutput instanceof IOPort)) {
throw new IllegalActionException(_actor,
mirrorInput.getContainer(),
"No matching port with name "
+ mirrorInput.getName());
}
Dependency dependency = causality.getDependency(
mirrorInput, dependentOutput);
forwardMap.put((IOPort) localOutput, dependency);
// Now handle the reverse dependencies.
Map<IOPort, Dependency> backwardMap = _reverseDependencies
.get(localOutput);
if (backwardMap == null) {
backwardMap = new HashMap<IOPort, Dependency>();
_reverseDependencies.put((IOPort) localOutput,
backwardMap);
}
backwardMap.put((IOPort) localInput, dependency);
}
}
}
// Next do equivalence classes.
// We iterate over the input ports, and for each one,
// find the ports for which it has equivalents in any
// associated causality.
_equivalenceClasses = new HashMap<IOPort, Collection<IOPort>>();
Collection<IOPort> localInputs = _actor.inputPortList();
for (IOPort localInput : localInputs) {
Collection<IOPort> equivalences = _equivalenceClasses
.get(localInput);
if (equivalences != null) {
// This input port is done.
continue;
}
equivalences = new HashSet<IOPort>();
// Iterate over all the associated interfaces.
for (CausalityInterface causality : _composedInterfaces) {
IOPort mirrorInput = (IOPort) ((Entity) causality
.getActor()).getPort(localInput.getName());
if (mirrorInput == null) {
throw new IllegalActionException(_actor,
localInput, "Expected matching port in "
+ causality.getActor()
.getFullName());
}
equivalences.addAll(_localMirrors(causality
.equivalentPorts(mirrorInput)));
}
// Set the equivalence class for all ports in the set.
for (IOPort equivalentPort : equivalences) {
_equivalenceClasses.put(equivalentPort, equivalences);
}
}
} finally {
((NamedObj) _actor).workspace().doneReading();
}
_dependencyVersion = workspaceVersion;
}
if (output == null) {
return null;
}
Map<IOPort, Dependency> inputMap = _forwardDependencies.get(input);
if (inputMap != null) {
Dependency result = inputMap.get(output);
if (result != null) {
return result;
}
}
// If there is no recorded dependency, then reply
// with the additive identity (which indicates no
// dependency).
return _defaultDependency.oPlusIdentity();
}
///////////////////////////////////////////////////////////////////
//// private methods ////
/** Return the local ports whose names match the ports in the
* specified collection.
* @param ports A collection of ports.
* @exception IllegalActionException If no matching port is found.
*/
private Collection<IOPort> _localMirrors(Collection<IOPort> ports)
throws IllegalActionException {
Set<IOPort> result = new HashSet<IOPort>();
for (IOPort port : ports) {
IOPort localPort = (IOPort) ((Entity) _actor).getPort(port
.getName());
if (localPort == null) {
throw new IllegalActionException(port.getContainer(), port,
"Expected matching port in " + _actor.getFullName());
}
result.add(localPort);
}
return result;
}
///////////////////////////////////////////////////////////////////
//// private fields ////
/** The set of causality interfaces that this one composes. */
private Set<CausalityInterface> _composedInterfaces = new HashSet<CausalityInterface>();
}