/* Filter for Property class changes
Copyright (c) 2002-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.moml.filter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import ptolemy.kernel.util.NamedObj;
import ptolemy.moml.MoMLFilter;
import ptolemy.moml.MoMLParser;
//////////////////////////////////////////////////////////////////////////
//// PropertyClassChanges
/** When this class is registered with the MoMLParser.setMoMLFilter()
method, it will cause MoMLParser to filter so that models from
earlier releases will run in the current release.
<p>This class will filter for classes with properties where the class
name has changed.
<p>For example, after Ptolemy II 2.0.1, the Expression actor
changed in such a way that the expression property changed from
being a Parameter to being a StringAttribute. To add this
change to this filter, we add a code to the static section at
the bottom of the file.
<pre>
// Expression: After 2.0.1, expression
// property is now a StringAttribute
HashMap expressionClassChanges = new HashMap();
// Key = property name, Value = new class name
expressionClassChanges.put("expression",
"ptolemy.kernel.util.StringAttribute");
</pre>
The expressionClassChange HashMap maps property names to the new
classname
<pre>
_actorsWithPropertyClassChanges
.put("ptolemy.actor.lib.Expression",
expressionClassChanges);
</pre>
The _actorsWithPropertyClassChanges HashMap contains all the classes
such as Expression that have changes and each class has a map
of the property changes that are to be made.
<p> Conceptually, how the code works is that when we see a class while
parsing, we check to see if the class is in _actorsWithPropertyClassChanges.
If the class was present in the HashMap, then as we go through the
code, we look for property names that need to have their classes changed.
@author Christopher Hylands, Edward A. Lee
@version $Id$
@since Ptolemy II 2.0
@Pt.ProposedRating Red (cxh)
@Pt.AcceptedRating Red (cxh)
*/
public class PropertyClassChanges implements MoMLFilter {
/** Clear the map of actors with property class changes.
*/
public static void clear() {
_actorsWithPropertyClassChanges = new HashMap();
}
/** Return new class names for properties that have been
* registered as having changed classes. This filter
* may also return null to remove the element.
* @param container The container for this attribute.
* in this method.
* @param element The XML element name.
* @param attributeName The name of the attribute.
* @param attributeValue The value of the attribute.
* @param xmlFile The file currently being parsed.
* @return The value of the attributeValue argument or
* a new value if the value has changed.
*/
public String filterAttributeValue(NamedObj container, String element,
String attributeName, String attributeValue, String xmlFile) {
// If you change this class, you should run before and after
// timing tests on large moml files, a good command to run
// is:
// $PTII/bin/ptolemy -test $PTII/ptolemy/domains/ct/demo/CarTracking/CarTracking.xml
// which will open up a large xml file and then close after 2 seconds.
//System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue);
// This method gets called many times by the MoMLParser,
// so we try to be smart about the number of comparisons
// and we try to group comparisons together so that we
// are not making the same comparison more than once.
if (attributeValue == null) {
// attributeValue == null is fairly common, so we check for
// that first
return null;
}
if (attributeName.equals("name")) {
// Save the name of the attribute for later use if we see a "class"
_lastNameSeen = attributeValue;
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav0.5: lastNameSeen: " + _lastNameSeen);
if (_currentlyProcessingActorWithPropertyClassChanges
&& (element != null) && element.equals("property")) {
if (_propertyMap.containsKey(attributeValue)) {
// We will do the above checks only if we found a
// class that had property class changes.
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav1: foundChange");
_newClass = (String) _propertyMap.get(attributeValue);
_foundChange = true;
} else {
// Saw a name that did not match.
// However, we might have other names that
// did match, so keep looking
//_currentlyProcessingActorWithPropertyClassChanges = false;
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav2: non-matching name");
_newClass = null;
_foundChange = false;
}
}
}
if (attributeName.equals("class")) {
if (_currentlyProcessingActorWithPropertyClassChanges
&& _foundChange) {
if ((container != null)
&& !container.getFullName().equals(
_currentActorFullName)
&& !container.getFullName().substring(0,
container.getFullName().lastIndexOf("."))
.equals(_currentActorFullName)
) {
// This is fix for an unusual bug involving
// space.Occupant.
// See test 1.1 in test/PropertyClassChanges.tcl
_currentlyProcessingActorWithPropertyClassChanges = false;
_newClass = null;
_foundChange = false;
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav3: Did not match, returning " + attributeValue);
return attributeValue;
}
// This if clause needs to be first so that we handle
// the PropertyClassChanges case where we have a
// _tableauFactory in a ModalModel that is not
// a ModalTableauFactory, but should be. An example
// of this is ct/demo/Pendulum3D/Pendulum3D.xml.
String temporaryNewClass = _newClass;
if (!attributeValue.equals(_newClass)) {
MoMLParser.setModified(true);
}
_newClass = null;
_foundChange = false;
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav4, returning temporaryNewClass" + temporaryNewClass);
return temporaryNewClass;
} else if (_actorsWithPropertyClassChanges
.containsKey(attributeValue)) {
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav4.5, found a class with a property class change");
// We found a class with a property class change.
_currentlyProcessingActorWithPropertyClassChanges = true;
if (container != null) {
_currentActorFullName = container.getFullName() + "."
+ _lastNameSeen;
} else {
_currentActorFullName = "." + _lastNameSeen;
}
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav5: found a class with a prop class change");
_propertyMap = (HashMap) _actorsWithPropertyClassChanges
.get(attributeValue);
} else if (_currentlyProcessingActorWithPropertyClassChanges
&& (container != null)
&& !container.getFullName().equals(_currentActorFullName)
/*&& !container.getFullName().substring(0,
container.getFullName().lastIndexOf(".")).equals(_currentActorFullName)*/
&& !container.getFullName().startsWith(
_currentActorFullName)) {
// We found another class in a different container
// while handling a class with port name changes
// System.out.println("<---filterAttributeValue: " + container + "\t"
// + attributeName + "\t" + attributeValue
// + "fav6: found another class in diff container");
_currentlyProcessingActorWithPropertyClassChanges = false;
}
}
return attributeValue;
}
/** Reset private variables.
* @param container The object created by this element.
* @param elementName The element name.
* @param currentCharData The character data, which appears
* only in the doc and configure elements
* @param xmlFile The file currently being parsed.
* @exception Exception if there is a problem substituting
* in the new value.
*/
public void filterEndElement(NamedObj container, String elementName,
StringBuffer currentCharData, String xmlFile) throws Exception {
// System.out.println("<---filterEndElement: "
// + ((container == null) ? "null" : container.getFullName())
// + "\t" + elementName + "\t" + currentCharData);
_foundChange = false;
}
/** Add a class to be filtered and the old and new property class
* types. Note that if you add a class with this method, then you
* must remove it with {@link #remove(String)}, calling
* "new PropertyClassChanges()" will not remove a class that was
* added with this method.
* @param className The name of the class to be filtered
* out, for example "ptolemy.copernicus.kernel.GeneratorAttribute".
* @param propertyClassMap The HashMap that has the property
* name as a key and the new class name as a value. If the value
* of the HashMap is null then the rest of the attribute is skipped.
* @see #remove(String)
*/
public void put(String className, HashMap propertyClassMap) {
_actorsWithPropertyClassChanges.put(className, propertyClassMap);
}
/** Remove a class to be filtered.
* @param className The name of the class to be filtered
* out, for example "ptolemy.copernicus.kernel.GeneratorAttribute".
* @see #put(String, HashMap)
*/
public void remove(String className) {
_actorsWithPropertyClassChanges.remove(className);
}
/** Return a string that describes what the filter does.
* @return the description of the filter that ends with a newline.
*/
public String toString() {
StringBuffer results = new StringBuffer(getClass().getName()
+ ": Update any actor port class names\n"
+ "that have been renamed.\n"
+ "Below are the actors that are affected, along "
+ "with the port name\nand the new classname:");
Iterator actors = _actorsWithPropertyClassChanges.keySet().iterator();
while (actors.hasNext()) {
String actor = (String) actors.next();
results.append("\t" + actor + "\n");
HashMap propertyMap = (HashMap) _actorsWithPropertyClassChanges
.get(actor);
if (propertyMap != null) {
Iterator propertyMapEntries = propertyMap.entrySet().iterator();
while (propertyMapEntries.hasNext()) {
Map.Entry properties = (Map.Entry) propertyMapEntries
.next();
String oldProperty = (String) properties.getKey();
String newProperty = (String) properties.getValue();
results.append("\t\t" + oldProperty + "\t -> "
+ newProperty + "\n");
}
}
}
return results.toString();
}
///////////////////////////////////////////////////////////////////
//// private variables ////
// Map of actor names a HashMap of property names to new classes.
private static HashMap _actorsWithPropertyClassChanges;
// The the full name of the actor we are currently processing
private String _currentActorFullName;
// Set to true if we are currently processing an actor with parameter
// class changes, set to false when we are done.
private boolean _currentlyProcessingActorWithPropertyClassChanges = false;
// Last "name" value seen, for use if we see a "class" for this actor
private String _lastNameSeen;
// The new class name for the property we are working on.
private String _newClass;
// Keep track of whether a change was found.
private boolean _foundChange;
// Cache of map from old property names to new class names for
// the actor we are working on.
private static HashMap _propertyMap;
static {
///////////////////////////////////////////////////////////
// Actors that have properties that have changed class.
_actorsWithPropertyClassChanges = new HashMap();
// Display
HashMap displayClassChanges = new HashMap();
// Key = property name, Value = new class name
displayClassChanges.put("title", "ptolemy.data.expr.StringParameter");
_actorsWithPropertyClassChanges.put("ptolemy.actor.lib.gui.Display",
displayClassChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sr.lib.gui.NonStrictDisplay",
displayClassChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.dt.kernel.text.TimedDisplay",
displayClassChanges);
// Expression
// This is a second generation change.
// Used to change it to a StringAttribute
HashMap expressionClassChanges = new HashMap();
// Key = property name, Value = new class name
expressionClassChanges.put("expression",
"ptolemy.kernel.util.StringAttribute");
_actorsWithPropertyClassChanges.put("ptolemy.actor.lib.Expression",
expressionClassChanges);
// _hideName in visible attributes.
HashMap hideNameClassChanges = new HashMap();
// Key = property name, Value = new class name
// NOTE: Ideally, we would create a
// ptolemy.data.expr.SingletonParameter with value
// true, but we have no mechanism to set the value,
// so we use an attribute.
hideNameClassChanges.put("_hideName",
"ptolemy.kernel.util.SingletonAttribute");
_actorsWithPropertyClassChanges.put("ptolemy.kernel.util.Attribute",
hideNameClassChanges);
// MathFunction
HashMap mathFunctionClassChanges = new HashMap();
// Key = property name, Value = new class name
mathFunctionClassChanges.put("function",
"ptolemy.data.expr.StringParameter");
_actorsWithPropertyClassChanges.put("ptolemy.actor.lib.MathFunction",
mathFunctionClassChanges);
// TrigFunction
HashMap trigFunctionClassChanges = new HashMap();
// Key = property name, Value = new class name
trigFunctionClassChanges.put("function",
"ptolemy.data.expr.StringParameter");
_actorsWithPropertyClassChanges.put("ptolemy.actor.lib.TrigFunction",
trigFunctionClassChanges);
// MatlabExpression
HashMap matlabClassChanges = new HashMap();
// Key = property name, Value = new class name
matlabClassChanges.put("expression",
"ptolemy.data.expr.StringParameter");
_actorsWithPropertyClassChanges.put("ptolemy.matlab.Expression",
matlabClassChanges);
// DirectoryListing
HashMap directoryListingClassChanges = new HashMap();
// Key = property name, Value = new class name
directoryListingClassChanges.put("directoryOrURL",
"ptolemy.actor.parameters.FilePortParameter");
_actorsWithPropertyClassChanges.put(
"ptolemy.actor.lib.io.DirectoryListing",
directoryListingClassChanges);
// LineWriter
HashMap lineWriterClassChanges = new HashMap();
// Key = property name, Value = new class name
lineWriterClassChanges.put("fileName",
"ptolemy.actor.parameters.FilePortParameter");
_actorsWithPropertyClassChanges.put("ptolemy.actor.lib.io.LineWriter",
lineWriterClassChanges);
// ModelReference
HashMap modelReferenceClassChanges = new HashMap();
// Key = property name, Value = new class name
modelReferenceClassChanges.put("modelFileOrURL",
"ptolemy.actor.parameters.FilePortParameter");
_actorsWithPropertyClassChanges.put(
"ptolemy.actor.lib.hoc.ModelReference",
modelReferenceClassChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.vergil.actor.lib.VisualModelReference",
modelReferenceClassChanges);
// SRDirector
HashMap srDirectorClassChanges = new HashMap();
// Key = property name, Value = new class name
srDirectorClassChanges.put("scheduler",
"ptolemy.data.expr.StringParameter");
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sr.kernel.Director", srDirectorClassChanges);
// There is only one BreakpointODESolver. Some old models have the
// wrong choices of break point ODE solvers. The following filters
// remove them.
// CTEmbeddedDirector
HashMap CTEmbeddedDirectorClassChanges = new HashMap();
// Key = property name, Value = new class name
CTEmbeddedDirectorClassChanges.put("breakpointODESolver", null);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.ct.kernel.CTEmbeddedDirector",
CTEmbeddedDirectorClassChanges);
// CTMixedSignalDirector
HashMap ctMixedSignalDirectorClassChanges = new HashMap();
// Key = property name, Value = new class name
ctMixedSignalDirectorClassChanges.put("breakpointODESolver", null);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.ct.kernel.CTMixedSignalDirector",
ctMixedSignalDirectorClassChanges);
// CTMultiSolverDirector
HashMap CTMultiSolverDirectorClassChanges = new HashMap();
// Key = property name, Value = new class name
CTMultiSolverDirectorClassChanges.put("breakpointODESolver", null);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.ct.kernel.CTMultiSolverDirector",
CTMultiSolverDirectorClassChanges);
// ModalModel
HashMap modalModelClassChanges = new HashMap();
// Key = property name, Value = new class name
modalModelClassChanges.put("directorClass",
"ptolemy.data.expr.StringParameter");
// Remove the _Director attribute, which does not help the modal model
// to decide which director to choose. This attribugte will be
// automatically created. This attribute will not appear in the MoML
// output any more.
// NOTE: Remove a property by setting the new class to null.
modalModelClassChanges.put("_Director", null);
modalModelClassChanges.put("_tableauFactory",
"ptolemy.vergil.fsm.modal.ModalTableauFactory");
// Note that we add ModalModel here then sometimes remove it
// in RemoveGraphical classes.
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.fsm.modal.ModalModel", modalModelClassChanges);
// ModalModel changes for the new model model
HashMap modalModelClassChanges2 = new HashMap();
// Key = property name, Value = new class name
modalModelClassChanges2.put("directorClass",
"ptolemy.data.expr.StringParameter");
// Remove the _Director attribute, which does not help the modal model
// to decide which director to choose. This attribugte will be
// automatically created. This attribute will not appear in the MoML
// output any more.
// NOTE: Remove a property by setting the new class to null.
modalModelClassChanges2.put("_Director", null);
// Here is the only difference between the filter for the old code
// and the new filter.
modalModelClassChanges2.put("_tableauFactory",
"ptolemy.vergil.modal.modal.ModalTableauFactory");
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.modal.modal.ModalModel",
modalModelClassChanges2);
// HashMap hdfClassChanges = new HashMap();
// hdfClassChanges.put("_Director", null);
// _actorsWithPropertyClassChanges.put(
// "ptolemy.domains.hdf.kernel.HDFFSMDirector", hdfClassChanges);
// LevelCrossingDetector
HashMap levelCrossingDetectorClassChanges = new HashMap();
levelCrossingDetectorClassChanges.put("useEventValue", null);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.ct.lib.LevelCrossingDetector",
levelCrossingDetectorClassChanges);
// ZeroCrossingDetector
HashMap zeroCrossingDetectorClassChanges = new HashMap();
zeroCrossingDetectorClassChanges.put("useEventValue", null);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.ct.lib.ZeroCrossingDetector",
zeroCrossingDetectorClassChanges);
// SDF actors don't record rates.
HashMap rateParameterChanges = new HashMap();
rateParameterChanges.put("tokenProductionRate", null);
rateParameterChanges.put("tokenConsumptionRate", null);
rateParameterChanges.put("tokenInitProduction", null);
_actorsWithPropertyClassChanges
.put("ptolemy.domains.sdf.lib.Autocorrelation",
rateParameterChanges);
_actorsWithPropertyClassChanges
.put("ptolemy.domains.sdf.lib.ArrayToSequence",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.BitsToInt", rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.Chop",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.CountTrues", rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.DownSample", rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.DoubleToMatrix", rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.FIR",
rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.FFT",
rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.IFFT",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.IntToBits", rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.LineCoder", rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.MatrixToDouble", rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.MatrixToSequence",
rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.Repeat",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.SampleDelay", rateParameterChanges);
_actorsWithPropertyClassChanges
.put("ptolemy.domains.sdf.lib.SequenceToArray",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.SequenceToMatrix",
rateParameterChanges);
_actorsWithPropertyClassChanges.put("ptolemy.domains.sdf.lib.UpSample",
rateParameterChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.sdf.lib.VariableFIR", rateParameterChanges);
// Transition
HashMap TransitionClassChanges = new HashMap();
TransitionClassChanges.put("relationList", null);
_actorsWithPropertyClassChanges
.put("ptolemy.domains.fsm.kernel.Transition",
TransitionClassChanges);
_actorsWithPropertyClassChanges.put(
"ptolemy.domains.modal.kernel.Transition",
TransitionClassChanges);
}
}