001/* Add a missing parameter.
002
003 Copyright (c) 2012-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.moml.filter;
029
030import java.util.HashMap;
031import java.util.Map;
032
033import ptolemy.kernel.util.IllegalActionException;
034import ptolemy.kernel.util.NamedObj;
035import ptolemy.moml.MoMLParser;
036
037///////////////////////////////////////////////////////////////////
038//// AddMissingParameter
039
040/** Add a missing parameter.
041
042  <p>If a SDFDirector does not have an iterations parameter, then
043  add one with the value "0", which is the default for Ptolemy II
044  8.0 and earlier.</p>
045
046  <p>FIXME: This class is similar to AddEditorFactory.
047  The two classes should be merged by parameterizing.
048
049 @author Christopher Brooks
050 @version $Id$
051 @since Ptolemy II 10.0
052 @Pt.ProposedRating Red (cxh)
053 @Pt.AcceptedRating Red (cxh)
054 */
055public class AddMissingParameter extends MoMLFilterSimple {
056    /**  Identify classes that need to have parameter added.
057     *
058     *  @param container  The container for this attribute.
059     *  in this method.
060     *  @param element The XML element name.
061     *  @param attributeName The name of the attribute.
062     *  @param attributeValue The value of the attribute.
063     *  @param xmlFile The file currently being parsed.
064     *  @return the value of the attributeValue argument.
065     */
066    @Override
067    public String filterAttributeValue(NamedObj container, String element,
068            String attributeName, String attributeValue, String xmlFile) {
069        // This method gets called many times by the MoMLParser,
070        // so we try to be smart about the number of comparisons
071        // and we try to group comparisons together so that we
072        // are not making the same comparison more than once.
073        if (attributeValue == null) {
074            // attributeValue == null is fairly common, so we check for
075            // that first
076            return null;
077        }
078
079        if (attributeName.equals("name")) {
080            // Save the name of the for later use if we see a "class"
081            _lastNameSeen = attributeValue;
082
083            if (_currentlyProcessingActorThatMayNeedUpdating) {
084                if (attributeValue.equals(_addProperty.propertyName)) {
085                    _currentlyProcessingActorThatMayNeedUpdating = false;
086                    _currentAttributeHasValue = false;
087                } else if (attributeValue
088                        .equals(_addProperty.onlyAddIfPresent)) {
089                    // We only add _editorFactory to parameters that
090                    // have _locations
091                    _currentAttributeHasValue = true;
092                }
093            }
094        }
095
096        // If you change this class, you should run before and after
097        // timing tests on large moml files, a good command to run
098        // is:
099        // $PTII/bin/ptolemy -test $PTII/ptolemy/domains/ct/demo/CarTracking/CarTracking.xml
100        // which will open up a large xml file and then close after 2 seconds.
101        if (attributeName.equals("class")) {
102            if (_namedObjsWithMissingProperties.containsKey(attributeValue)) {
103                _currentlyProcessingActorThatMayNeedUpdating = true;
104
105                //System.out.println("AddMissingParameter: " + _lastNameSeen + " " + attributeValue);
106                if (container != null) {
107                    _currentActorFullName = container.getFullName() + "."
108                            + _lastNameSeen;
109                } else {
110                    _currentActorFullName = "." + _lastNameSeen;
111                }
112                _addProperty = _namedObjsWithMissingProperties
113                        .get(attributeValue);
114            } else if (_currentlyProcessingActorThatMayNeedUpdating
115                    && container != null
116                    && !container.getFullName().equals(_currentActorFullName)
117                    && !container.getFullName()
118                            .startsWith(_currentActorFullName)) {
119                // We found another class in a different container
120                // while handling a class with port name changes, so
121                _currentlyProcessingActorThatMayNeedUpdating = false;
122                //System.out.println("AddMissingParameter: " + _lastNameSeen + " " + attributeValue + " setting to _currentlyProcessingActorThatMayNeedUpdating to false");
123                _currentAttributeHasValue = false;
124                _addProperty = null;
125            }
126        }
127
128        return attributeValue;
129    }
130
131    /** Make modifications to the specified container, which is
132     *  defined in a MoML element with the specified name.
133     *  @param container The object created by this element.
134     *  @param elementName The element name.
135     *  @param currentCharData The character data, which appears
136     *   only in the doc and configure elements
137     *  @param xmlFile The file currently being parsed.
138     *  @exception Exception if there is a problem substituting
139     *  in the new value.
140     *  @deprecated Use {@link #filterEndElement(NamedObj, String, StringBuffer, String, MoMLParser)}
141     * instead and pass a MoMLParser.
142     */
143    @Deprecated
144    @Override
145    public void filterEndElement(NamedObj container, String elementName,
146            StringBuffer currentCharData, String xmlFile) throws Exception {
147        filterEndElement(container, elementName, currentCharData, xmlFile,
148                new MoMLParser());
149    }
150
151    /** Make modifications to the specified container, which is
152     *  defined in a MoML element with the specified name.
153     *  @param container The object created by this element.
154     *  @param elementName The element name.
155     *  @param currentCharData The character data, which appears
156     *   only in the doc and configure elements
157     *  @param xmlFile The file currently being parsed.
158     *  @param parser The parser in which MoML is optionally evaluated.
159     *  @exception Exception if there is a problem substituting
160     *  in the new value.
161     */
162    @Override
163    public void filterEndElement(NamedObj container, String elementName,
164            StringBuffer currentCharData, String xmlFile, MoMLParser parser)
165            throws Exception {
166        //          System.out.println("AddMissingParameter: filterEndElement: "
167        //                  + _currentlyProcessingActorThatMayNeedUpdating
168        //                  + " elementName: " + (elementName == null? "null" : elementName)
169        //                  + "\n            container: " + (container == null ? "null" : container.getFullName())
170        //                  + "\n currentActorFullName: " + _currentActorFullName);
171
172        if (!_currentlyProcessingActorThatMayNeedUpdating) {
173            return;
174        } else if (_addProperty != null
175                && (_addProperty.onlyAddIfPresent != null
176                        && _currentAttributeHasValue
177                        || _addProperty.onlyAddIfPresent == null)
178                && elementName != null && elementName.equals("property")
179                && container != null
180                && container.getFullName().equals(_currentActorFullName)) {
181
182            //System.out.println("AddMissingParameter: filterEndElement: Processing!!!");
183            _currentlyProcessingActorThatMayNeedUpdating = false;
184            _currentAttributeHasValue = false;
185
186            // We use parse moml here so that we can avoid adding
187            // dependencies.  For SDFIterations, we could just
188            // add an attribute, but this class should be
189            // extended to be more general.
190
191            // setContext calls parser.reset()
192            parser.setContext(container);
193
194            String moml = _addProperty.moml;
195            _addProperty = null;
196
197            //System.out.println("AddMissingParameter: filterEndElement: moml: " + moml);
198            try {
199                // Do not call parse(moml) here, since that method
200                // will fail if we are in an applet because it tries
201                // to read user.dir
202                parser.parse(null, moml);
203                MoMLParser.setModified(true);
204            } catch (Exception ex) {
205                throw new IllegalActionException(null, ex,
206                        "Failed to parse\n" + moml);
207            }
208        }
209    }
210
211    /** Return a string that describes what the filter does.
212     *  @return the description of the filter that ends with a newline.
213     */
214    @Override
215    public String toString() {
216        StringBuffer results = new StringBuffer(getClass().getName()
217                + ": If a NamedObj is missing a property, then add it.\n"
218                + "Optionally, only add the property if another property, "
219                + "such as _location is present.\n"
220                + "Below are the property names, the optional property and the moml:\n");
221        for (Map.Entry<String, AddProperty> entry : _namedObjsWithMissingProperties
222                .entrySet()) {
223            String namedObjName = entry.getKey();
224            AddProperty addProperty = entry.getValue();
225            results.append(
226                    namedObjName + "\t -> " + addProperty.propertyName + "\t"
227                            + (addProperty.onlyAddIfPresent == null ? "null"
228                                    : addProperty.onlyAddIfPresent)
229                            + "\n\t" + addProperty.moml + "\n");
230        }
231        return results.toString();
232    }
233
234    /** A Structure that contains the property name, the moml and
235     *  the optional property name that, if non-null, must be present.
236     */
237    private static class AddProperty {
238        AddProperty(String propertyName, String moml, String onlyAddIfPresent) {
239            this.propertyName = propertyName;
240            this.moml = moml;
241            this.onlyAddIfPresent = onlyAddIfPresent;
242        }
243
244        /** The property name to be added if it is missing. */
245        public String propertyName;
246        /** The moml to be added */
247        public String moml;
248        /** Only add the parameter if the attribute has a parameter
249         *  that has the value equal to the value of onlyAddIfPresent.
250         *  If this field is null, then the parameter is always added
251         *  if propertyName is not present.
252         */
253        public String onlyAddIfPresent;
254    }
255
256    ///////////////////////////////////////////////////////////////////
257    ////                         private variables                 ////
258
259    // Map of NamedObject names to an object of
260    private static HashMap<String, AddProperty> _namedObjsWithMissingProperties;
261
262    // Cache of parameters name to be added.
263    private static AddProperty _addProperty;
264
265    // The the full name of the actor we are currently processing
266    private String _currentActorFullName;
267
268    // Set to true if the current attribute has a value that matches.
269    // This variable is used to determine whether we need to add an
270    // iteration
271    private boolean _currentAttributeHasValue = false;
272
273    // Set to true if we are currently processing an actor that may
274    // need an update, set to false when we are done.
275    private boolean _currentlyProcessingActorThatMayNeedUpdating = false;
276
277    // Last "name" value seen, for use if we see a "class".
278    private String _lastNameSeen;
279
280    static {
281        _namedObjsWithMissingProperties = new HashMap<String, AddProperty>();
282
283        // SDFDirector
284        AddProperty sdfDirectorChanges = new AddProperty("iterations",
285                "<property " + "name=\"iterations\" "
286                        + "class=\"ptolemy.data.expr.Parameter\" "
287                        + "value=\"0\"/>",
288                null);
289        _namedObjsWithMissingProperties.put(
290                "ptolemy.domains.sdf.kernel.SDFDirector", sdfDirectorChanges);
291
292        // EditorFactory
293        AddProperty editorFactoryChanges = new AddProperty("_editorFactory",
294                "<property name=\"_editorFactory\""
295                        + " class=\"ptolemy.vergil.toolbox."
296                        + "VisibleParameterEditorFactory\"/>",
297                "_location");
298
299        _namedObjsWithMissingProperties.put("ptolemy.data.expr.Parameter",
300                editorFactoryChanges);
301    }
302}