001/* Add "this." to certain places in the script parameter of a JavaScript actor.
002
003 Copyright (c) 2016-2018 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.lang.reflect.Method;
031
032import ptolemy.kernel.util.NamedObj;
033import ptolemy.kernel.util.Settable;
034import ptolemy.moml.MoMLParser;
035
036///////////////////////////////////////////////////////////////////
037//// JavaScriptThisUpdate
038
039/**
040 * Add "this." to certain places in the script parameter of a JavaScript actor.
041 *
042 * <p>This class will eventually be removed after we have updated the files.</p>
043 *
044 * <p>To update a model, try:</p>
045 * <pre>
046 * $PTII/bin/ptinvoke ptolemy.vergil.basic.imprt.accessor.ReloadAccessors model.xml
047 * </pre>
048 *
049 * To update all the models that contain JavaScript actors:
050 * <pre>
051 * # Generate a list of all the text files in the ptII tree, excluding certain directories like vendors.
052 * $PTII/adm/bin/ptIItxtfiles &lt;&amp; /tmp/f
053 *
054 * # Generate a list of all of the .xml files.
055 * cat /tmp/f | egrep '.xml$' &lt; /tmp/x
056 *
057 * # Generate a list of all the .xml files that contain the JavaScript actor.
058 * cat /tmp/x | xargs egrep 'ptolemy.actor.lib.jjs.JavaScript' | awk -F ':' '{print $1}' | sort | uniq &lt; /tmp/javascriptx
059 *
060 * # Reload the accessors on all the .xml files that contain the JavaScript actor,
061 * # which as a side effect runs the Backward Compatibility script
062 * cat /tmp/jsaccessorx | xargs $PTII/bin/ptinvoke ptolemy.vergil.basic.imprt.accessor.ReloadAccessors
063 * </pre>
064 *
065 * <p>We use <code>ptinvoke</code> to set the classpath.</p>
066 *
067 * <p>ReloadAccessors, opens a model, reloads all the accessors (if any) and
068 * saves the model.  While opening the model, the BackwardCompatibility MoML
069 * filters are run.  This class can be one of those filters.</p>
070 *
071 * @author Christopher Brooks
072 * @version $Id$
073 * @since Ptolemy II 11.0
074 * @Pt.ProposedRating Yellow (eal)
075 * @Pt.AcceptedRating Red (cxh)
076 */
077public class JavaScriptThisUpdate extends MoMLFilterSimple {
078
079    ///////////////////////////////////////////////////////////////////
080    ////                         public methods                    ////
081
082    /** Handle parameter name changes.
083     *  @param container  The container for XML element.
084     *  @param element The XML element name.
085     *  @param attributeName The name of the attribute.
086     *  @param attributeValue The value of the attribute.
087     *  @param xmlFile The file currently being parsed.
088     *  @return A new value for the attribute, or the same value
089     *   to leave it unchanged, or null to cause the current element
090     *   to be ignored (unless the attributeValue argument is null).
091     */
092    @Override
093    public String filterAttributeValue(NamedObj container, String element,
094            String attributeName, String attributeValue, String xmlFile) {
095        return attributeValue;
096    }
097
098    /** If the container is a property named "script" contained
099     *  by the JavaScript actor, then add "this." to certain
100     *  function calls.
101     *  @param container The object defined by the element that this
102     *   is the end of.
103     *  @param elementName The element name.
104     *  @param currentCharData The character data, which appears
105     *   only in the doc and configure elements
106     *  @param xmlFile The file currently being parsed.
107     *  @exception Exception if there is a problem substituting
108     *  in the new value.
109     */
110    @Override
111    public void filterEndElement(NamedObj container, String elementName,
112            StringBuffer currentCharData, String xmlFile) throws Exception {
113
114        // Fix the background color of the ViewScreen actor.
115        // Note that the ViewScreen actor also has a name change.
116        if (container != null && container.getName().equals("script")) {
117            NamedObj actor = container.getContainer();
118
119            if (actor != null && actor.getClass().getName()
120                    .startsWith("ptolemy.actor.lib.jjs.JavaScript")) {
121                String value = ((Settable) container).getExpression().trim();
122
123                // Prepend "this." to keywords that have leading whitespace.
124                for (int i = 0; i < _keywords.length; i++) {
125                    value = value.replaceAll(" " + _keywords[i] + "\\(",
126                            " this." + _keywords[i] + "(");
127                    value = value.replaceAll("\t" + _keywords[i] + "\\(",
128                            "\tthis." + _keywords[i] + "(");
129                }
130
131                // If the old and new values are different, then print them out
132                // and mark the container as modified.
133                String previousValue = ((Settable) container).getExpression()
134                        .trim();
135                if (!value.equals(previousValue)) {
136                    System.out.println(
137                            "JavaScriptThisUpdate: " + actor.getFullName()
138                                    + " has a script:\n" + previousValue);
139                    System.out.println("That has been updated to:\n" + value);
140                    // $PTII/ptolemy/util/test/Diff.java is not necessarily present.
141                    if (_diff != null) {
142                        System.out.println("The diff is:\n");
143                        System.out.println(_diff.invoke(null,
144                                ((Settable) container).getExpression().trim(),
145                                value));
146                    }
147                    ((Settable) container).setExpression(value);
148                    MoMLParser.setModified(true);
149                }
150            }
151        }
152    }
153
154    /** Return a string that describes what the filter does.
155     *  @return A description of the filter (ending with a newline).
156     */
157    @Override
158    public String toString() {
159        return getClass().getName()
160                + ": Update script parameter of the JavaScript actor "
161                + "by adding \"this.\" to certain locations";
162    }
163
164    // Keywords that get "this." prepended if the have leading
165    // whitespace.
166    private static String[] _keywords = { "addInputHandler", "connect",
167            "extend", "get", "getParameter", "getResource", "implement",
168            "input", "instantiate", "output", "parameter", "removeInputHandler",
169            "send", "setDefault", "setParameter" };
170
171    static {
172        try {
173            Class diffClass = Class.forName("ptolemy.util.test.Diff");
174            _diff = diffClass.getDeclaredMethod("diff",
175                    new Class[] { String.class, String.class });
176        } catch (ClassNotFoundException ex) {
177            System.err.println(
178                    "JavaScriptThisUpdated could not find ptolemy.util.test.Diff, so diffs will not be printed.");
179        } catch (NoSuchMethodException ex2) {
180            System.err.println(
181                    "JavaScriptThisUpdated could not find ptolemy.util.test.Diff.diff(String, String), so diffs will not be printed.");
182        }
183
184    }
185
186    ///////////////////////////////////////////////////////////////////
187    ////                         protected variables               ////
188
189    /** The ptolemy.util.test.Diff.diff(String, String) method. */
190    protected static Method _diff = null;
191}