001/* An application that executes non-graphical
002 models specified on the command line.
003
004 Copyright (c) 2009-2013 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.moml;
030
031import java.io.File;
032
033import ptolemy.actor.CompositeActor;
034import ptolemy.actor.Director;
035import ptolemy.actor.Manager;
036import ptolemy.kernel.util.Attribute;
037import ptolemy.kernel.util.IllegalActionException;
038import ptolemy.kernel.util.Settable;
039import ptolemy.moml.filter.BackwardCompatibility;
040import ptolemy.moml.filter.RemoveGraphicalClasses;
041
042//////////////////////////////////////////////////////////////////////////
043//// MoMLCommandLineApplication
044
045/** A application that reads command line arguments that set parameters
046    and a .xml file naming a model, sets the parameters and runs the model.
047
048 <p>MoMLApplication sets the look and feel, which starts up Swing,
049 so we can't use MoMLApplication for non-graphical simulations.
050
051 <p>The parent class, @{link ptolemy.moml.MoMLSimpleApplication} does
052 not handle command line parameters that set parameters
053
054 For example to use this class, try:
055 <pre>
056 java -classpath $PTII ptolemy.moml.MoMLCommandLineApplication -myParameter '&amp;quot;Hello, World.&amp;quot;' test/MoMLCommandLineApplicationTest.xml
057 </pre>
058or
059 <pre>
060 $PTII/bin/ptinvoke ptolemy.moml.MoMLCommandLineApplication -myParameter '"&amp;quot;Hellow, World.&amp;quot;"' test/MoMLCommandLineApplicationTest.xml
061 </pre>
062 <p>
063 If a Ptolemy model is instantiated on the command line, either
064 by giving a MoML file or a -class argument, then parameters of that
065 model can be set on the command line.  The syntax is:
066 <pre>
067 $PTII/bin/ptinvoke ptolemy.moml.MoMLCommandLineApplication -<i>parameterName</i> <i>value</i> <i>modelFile.xml</i>
068 </pre>
069 where <i>parameterName</i> is the name of a parameter relative to
070 the top level of a model or the director of a model.  For instance,
071 if foo.xml defines a toplevel entity named <code>x</code> and
072 <code>x</code> contains an entity named <code>y</code> and a
073 parameter named <code>a</code>, and <code>y</code> contains a
074 parameter named <code>b</code>, then:
075 <pre>
076 $PTII/bin/ptinvoke ptolemy.moml.MoMLCommandLineApplication  -a 5 -y.b 10 foo.xml
077 </pre>
078 would set the values of the two parameters.
079
080 <p>Note that strings need to be carefully backslashed, so to set a
081 parameter named <code>c</code> to the string <code>"bar"</code> it
082 might be necessary to do something like:
083 <pre>
084 $PTII/bin/ptinvoke ptolemy.moml.MoMLCommandLineApplication -a 5 -y.b 10 -c '"&amp;quot;bar&amp;quot;" foo.xml
085 </pre>
086 The reason the single quotes and double quotes are necessary is because <code>ptinvoke</code>
087 is a shell script which tends to strip off the double quotes.
088
089 @author Christopher Brooks
090 @version $Id$
091 @since Ptolemy II 8.0
092 @Pt.ProposedRating Red (cxh)
093 @Pt.AcceptedRating Red (eal)
094 */
095public class MoMLCommandLineApplication extends MoMLSimpleApplication {
096
097    /** Parse a MoML file that contains a model, update the parameters
098     *  and run the model.
099     *  @param args An array of strings, where the last element names
100     *  a MoML file that contains a Ptolemy II model.  The string should be
101     *  a relative pathname.  Other elements may name model parameters
102     *  and values for the parameters.  Parameter names should begin
103     *  with a "-" and be followed by the value for the parameter.
104     *  @exception Throwable If there was a problem parsing
105     *  or running the model.
106     */
107    public MoMLCommandLineApplication(String[] args) throws Throwable {
108        MoMLParser parser = new MoMLParser();
109
110        // The test suite calls MoMLSimpleApplication multiple times,
111        // and the list of filters is static, so we reset it each time
112        // so as to avoid adding filters every time we run an auto test.
113        // We set the list of MoMLFilters to handle Backward Compatibility.
114        MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters());
115
116        // Filter out any graphical classes.
117        MoMLParser.addMoMLFilter(new RemoveGraphicalClasses());
118
119        // If there is a MoML error, then throw the exception as opposed
120        // to skipping the error.  If we call StreamErrorHandler instead,
121        // then the nightly build may fail to report MoML parse errors
122        // as failed tests
123        //parser.setErrorHandler(new StreamErrorHandler());
124        // We use parse(URL, URL) here instead of parseFile(String)
125        // because parseFile() works best on relative pathnames and
126        // has problems finding resources like files specified in
127        // parameters if the xml file was specified as an absolute path.
128        CompositeActor toplevel = (CompositeActor) parser.parse(null,
129                new File(args[args.length - 1]).toURI().toURL());
130
131        _manager = new Manager(toplevel.workspace(), "MoMLSimpleApplication");
132        toplevel.setManager(_manager);
133        toplevel.addChangeListener(this);
134        _manager.addExecutionListener(this);
135
136        // This code is very similar to code in
137        // ptolemy.actor.gui.MoMLApplication
138        String parameterName = null;
139        String parameterValue = null;
140        for (int i = 0; i < args.length - 1; i++) {
141            String arg = args[i];
142            if (arg.trim().startsWith("-")) {
143                if (i >= args.length - 1) {
144                    throw new IllegalActionException(
145                            "Cannot set " + "parameter " + arg
146                                    + " when no value is " + "given.");
147                }
148
149                // Save in case this is a parameter name and value.
150                parameterName = arg.substring(1);
151                parameterValue = args[i + 1];
152
153                // First, see if there are any attributes in the top level
154                Attribute attribute = toplevel.getAttribute(parameterName);
155                if (attribute instanceof Settable) {
156                    // Use a MoMLChangeRequest so that visual rendition (if
157                    // any) is updated and listeners are notified.
158                    String moml = "<property name=\"" + parameterName
159                            + "\" value=\"" + parameterValue + "\"/>";
160                    MoMLChangeRequest request = new MoMLChangeRequest(this,
161                            toplevel, moml);
162                    toplevel.requestChange(request);
163                }
164
165                // Now try for parameters in the director
166                Director director = toplevel.getDirector();
167
168                if (director != null) {
169                    attribute = director.getAttribute(parameterName);
170
171                    if (attribute instanceof Settable) {
172                        // Use a MoMLChangeRequest so that visual rendition (if
173                        // any) is updated and listeners are notified.
174                        String moml = "<property name=\"" + parameterName
175                                + "\" value=\"" + parameterValue + "\"/>";
176                        MoMLChangeRequest request = new MoMLChangeRequest(this,
177                                director, moml);
178                        director.requestChange(request);
179                    }
180                }
181                i++;
182            } else {
183                // Unrecognized option.
184                throw new IllegalActionException("Unrecognized option: " + arg);
185            }
186        }
187
188        // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4178
189        // "error handling with MoMLCommandLineApplication"
190        _manager.run();
191
192        // PtExecuteApplication uses _activeCount to determine when
193        // the models are done.  We can't do that here because
194        // executeError() might be called from a different thread.
195        // PtExecuteApplication handles this by deferring the change
196        // to the Swing event thread.  We don't have a Swing event thread,
197        // so we are stuck with a busy loop.
198        while (!_executionFinishedOrError) {
199            Thread.yield();
200        }
201
202        if (_sawThrowable != null) {
203            throw _sawThrowable;
204        }
205    }
206
207    /** Create an instance of a single model and run it.
208     *  @param args The command-line arguments naming the .xml file to run
209     */
210    public static void main(String[] args) {
211        try {
212            new MoMLCommandLineApplication(args);
213        } catch (Throwable ex) {
214            System.err.println("Command failed: " + ex);
215            ex.printStackTrace();
216        }
217    }
218}