001/* Add icons to certain actors
002
003 Copyright (c) 2002-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.Iterator;
032
033import ptolemy.kernel.util.IllegalActionException;
034import ptolemy.kernel.util.NamedObj;
035import ptolemy.moml.MoMLParser;
036
037///////////////////////////////////////////////////////////////////
038//// AddIcon
039
040/** Certain actors have specialized icons that display the value of
041 one of the parameters.  This filter adds icons to those actors when
042 necessary.
043
044 @author Christopher Hylands, Edward A. Lee
045 @version $Id$
046 @since Ptolemy II 2.0
047 @Pt.ProposedRating Red (cxh)
048 @Pt.AcceptedRating Red (cxh)
049 */
050public class AddIcon extends MoMLFilterSimple {
051    /**  If the attributeName is "class" and attributeValue names a
052     *        class that has had its port names changed between releases,
053     *  then substitute in the new port names.
054     *
055     *  @param container  The container for this attribute.
056     *  in this method.
057     *  @param element The XML element name.
058     *  @param attributeName The name of the attribute.
059     *  @param attributeValue The value of the attribute.
060     *  @param xmlFile The file currently being parsed.
061     *  @return the value of the attributeValue argument.
062     */
063    @Override
064    public String filterAttributeValue(NamedObj container, String element,
065            String attributeName, String attributeValue, String xmlFile) {
066        // This method gets called many times by the MoMLParser,
067        // so we try to be smart about the number of comparisons
068        // and we try to group comparisons together so that we
069        // are not making the same comparison more than once.
070        if (attributeValue == null) {
071            // attributeValue == null is fairly common, so we check for
072            // that first
073            return null;
074        }
075
076        if (attributeName.equals("name")) {
077            // Save the name of the for later use if we see a "class"
078            _lastNameSeen = attributeValue;
079
080            if (_currentlyProcessingActorThatMayNeedAnIcon
081                    && attributeValue.equals("_icon")) {
082                // We are processing an annotation and it already
083                // has _icon
084                _currentlyProcessingActorThatMayNeedAnIcon = false;
085            }
086        }
087
088        // If you change this class, you should run before and after
089        // timing tests on large moml files, a good command to run
090        // is:
091        // $PTII/bin/ptolemy -test $PTII/ptolemy/domains/ct/demo/CarTracking/CarTracking.xml
092        // which will open up a large xml file and then close after 2 seconds.
093        if (attributeName.equals("class")) {
094            if (_actorsThatShouldHaveIcons.containsKey(attributeValue)) {
095                // We found a class that needs an _icon
096                _currentlyProcessingActorThatMayNeedAnIcon = true;
097
098                if (container != null) {
099                    _currentActorFullName = container.getFullName() + "."
100                            + _lastNameSeen;
101                } else {
102                    _currentActorFullName = "." + _lastNameSeen;
103                }
104
105                _iconMoML = (String) _actorsThatShouldHaveIcons
106                        .get(attributeValue);
107            } else if (_currentlyProcessingActorThatMayNeedAnIcon
108                    && container != null
109                    && !container.getFullName().equals(_currentActorFullName)
110                    && !container.getFullName()
111                            .startsWith(_currentActorFullName)) {
112                // We found another class in a different container
113                // while handling a class with port name changes, so
114                _currentlyProcessingActorThatMayNeedAnIcon = false;
115            }
116        }
117
118        return attributeValue;
119    }
120
121    /** Make modifications to the specified container, which is
122     *  defined in a MoML element with the specified name.
123     *  @param container The object created by this element.
124     *  @param elementName The element name.
125     *  @param currentCharData The character data, which appears
126     *   only in the doc and configure elements
127     *  @param xmlFile The file currently being parsed.
128     *  @exception Exception if there is a problem substituting
129     *  in the new value.
130     *  @deprecated Use {@link #filterEndElement(NamedObj, String, StringBuffer, String, MoMLParser)}
131     * instead and pass a MoMLParser.
132     */
133    @Deprecated
134    @Override
135    public void filterEndElement(NamedObj container, String elementName,
136            StringBuffer currentCharData, String xmlFile) throws Exception {
137        filterEndElement(container, elementName, currentCharData, xmlFile,
138                new MoMLParser());
139    }
140
141    /** Make modifications to the specified container, which is
142     *  defined in a MoML element with the specified name.
143     *  @param container The object created by this element.
144     *  @param elementName The element name.
145     *  @param currentCharData The character data, which appears
146     *   only in the doc and configure elements
147     *  @param xmlFile The file currently being parsed.
148     *  @param parser The parser in which MoML is optionally evaluated.
149     *  @exception Exception if there is a problem substituting
150     *  in the new value.
151     */
152    @Override
153    public void filterEndElement(NamedObj container, String elementName,
154            StringBuffer currentCharData, String xmlFile, MoMLParser parser)
155            throws Exception {
156        if (_currentlyProcessingActorThatMayNeedAnIcon
157                && elementName.equals("entity") && container != null
158                && container.getFullName().equals(_currentActorFullName)) {
159            _currentlyProcessingActorThatMayNeedAnIcon = false;
160
161            // Note that setContext() calls reset() so we don't want
162            // to do it on the main parser.
163            parser.setContext(container);
164
165            try {
166                // Do not call parse(_iconMoML) here, since that method
167                // will fail if we are in an applet because it tries
168                // to read user.dir
169                parser.parse(null, _iconMoML);
170                MoMLParser.setModified(true);
171            } catch (Exception ex) {
172                throw new IllegalActionException(null, ex,
173                        "Failed to parse\n" + _iconMoML);
174            }
175        }
176    }
177
178    /** Return a string that describes what the filter does.
179     *  @return the description of the filter that ends with a newline.
180     */
181    @Override
182    public String toString() {
183        StringBuffer results = new StringBuffer(getClass().getName()
184                + ": Add specialized icons that display the value\n"
185                + "of one of the parameters.\n" + "The affected actors are:\n");
186        Iterator actors = _actorsThatShouldHaveIcons.keySet().iterator();
187
188        while (actors.hasNext()) {
189            results.append("\t" + (String) actors.next() + "\n");
190        }
191
192        return results.toString();
193    }
194
195    ///////////////////////////////////////////////////////////////////
196    ////                         private variables                 ////
197    // Map of actors that should have _icon
198    private static HashMap _actorsThatShouldHaveIcons;
199
200    // The the full name of the actor we are currently processing
201    private String _currentActorFullName;
202
203    // Set to true if we are currently processing an actor that may
204    // need _icon added, set to false when we are done.
205    private boolean _currentlyProcessingActorThatMayNeedAnIcon = false;
206
207    // The moml that we should substitute in if we need to add
208    // an _icon
209    private String _iconMoML;
210
211    // Last "name" value seen, for use if we see a "class".
212    private String _lastNameSeen;
213
214    static {
215        ///////////////////////////////////////////////////////////
216        // Actors that should have _icon
217        _actorsThatShouldHaveIcons = new HashMap();
218
219        // In alphabetic order by actor class name.
220        _actorsThatShouldHaveIcons.put("ptolemy.actor.lib.Const",
221                "<property name=\"_icon\" "
222                        + "class=\"ptolemy.vergil.icon.BoxedValueIcon\">\n"
223                        + "<property name=\"attributeName\" value=\"value\"/>\n"
224                        + "<property name=\"displayWidth\" value=\"40\"/>\n"
225                        + "</property>\n");
226
227        // In alphabetic order by actor class name.
228        _actorsThatShouldHaveIcons.put("ptolemy.actor.lib.Expression",
229                "<property name=\"_icon\" "
230                        + "class=\"ptolemy.vergil.icon.BoxedValueIcon\">\n"
231                        + "<property name=\"attributeName\" value=\"expression\"/>\n"
232                        + "<property name=\"displayWidth\" value=\"60\"/>\n"
233                        + "</property>\n");
234
235        String functionIcon = "<property name=\"_icon\" "
236                + "class=\"ptolemy.vergil.icon.AttributeValueIcon\">\n"
237                + "<property name=\"attributeName\" value=\"function\"/>\n"
238                + "</property>\n";
239
240        _actorsThatShouldHaveIcons.put("ptolemy.actor.lib.MathFunction",
241                functionIcon);
242
243        _actorsThatShouldHaveIcons.put("ptolemy.actor.lib.Scale",
244                "<property name=\"_icon\" "
245                        + "class=\"ptolemy.vergil.icon.AttributeValueIcon\">\n"
246                        + "<property name=\"attributeName\" value=\"factor\"/>\n"
247                        + "</property>\n");
248
249        _actorsThatShouldHaveIcons.put("ptolemy.actor.lib.TrigFunction",
250                functionIcon);
251    }
252}