001/* An application that executes non-graphical
002 models specified on the command line.
003
004 Copyright (c) 2008-2014 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;
032import java.util.List;
033
034import ptolemy.actor.TypedCompositeActor;
035import ptolemy.kernel.ComponentEntity;
036import ptolemy.kernel.util.ChangeListener;
037import ptolemy.kernel.util.ChangeRequest;
038import ptolemy.moml.filter.BackwardCompatibility;
039
040///////////////////////////////////////////////////////////////////
041//// ConvertToLazy
042
043/** Read a specified MoML file and convert all instances of
044 *  TypedCompositeActor that contain more than a specified
045 *  number of entities to LazyTypedCompositeActor. The
046 *  converted model's MoML is produced on standard out.
047 *  To use this on the command line, invoke as follows:
048 *  <pre>
049 *     $PTII/bin/convertToLazy inputMoML.xml <i>numberOfEntities</i> &gt; outputMoML.xml
050 *  </pre>
051 *  or
052 *  <pre>
053 *     java -classpath $PTII ptolemy.moml.ConvertToLazy inputMoML.xml <i>numberOfEntities</i> &gt; outputMoML.xml
054 *  </pre>
055 *  If the <i>numberOfEntities</i> argument is not supplied, then it
056 *  defaults to 100.
057
058 @author Edward A. Lee
059 @version $Id$
060 @since Ptolemy II 8.0
061 @Pt.ProposedRating Red (cxh)
062 @Pt.AcceptedRating Red (eal)
063 */
064public class ConvertToLazy implements ChangeListener {
065
066    /** Parse the xml file and convert it.
067     *  @param xmlFileName A string that refers to an MoML file that
068     *  contains a Ptolemy II model.  The string should be
069     *  a relative pathname.
070     *  @param threshold The number of contained entities that a composite
071     *   should have to be converted to a lazy composite.
072     *  @exception Throwable If there was a problem parsing
073     *  or running the model.
074     */
075    public ConvertToLazy(String xmlFileName, int threshold) throws Throwable {
076        MoMLParser parser = new MoMLParser();
077
078        // Save the current MoMLFilters before conversion so that if
079        // we call this class from within a larger application, we don't
080        // change the filters.
081        List oldFilters = MoMLParser.getMoMLFilters();
082        try {
083            // The test suite calls UseLazyCompositeApplication multiple times,
084            // and the list of filters is static, so we reset it each time
085            // so as to avoid adding filters every time we run an auto test.
086            // We set the list of MoMLFilters to handle Backward Compatibility.
087            MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters());
088
089            // If there is a MoML error, then throw the exception as opposed
090            // to skipping the error.  If we call StreamErrorHandler instead,
091            // then the nightly build may fail to report MoML parse errors
092            // as failed tests
093            //parser.setErrorHandler(new StreamErrorHandler());
094            // We use parse(URL, URL) here instead of parseFile(String)
095            // because parseFile() works best on relative pathnames and
096            // has problems finding resources like files specified in
097            // parameters if the xml file was specified as an absolute path.
098            TypedCompositeActor toplevel = (TypedCompositeActor) parser
099                    .parse(null, new File(xmlFileName).toURI().toURL());
100            convert(toplevel, threshold);
101
102            // We export and then reparse and then export again so
103            // that the resulting MoML has the <configure>
104            // ...</configure> blocks.  If just exportMoML, then the moml will
105            // not have the <configure> ...</configure> blocks because the
106            // LazyTypedCompositeActor._exportMoMLContents() method is not
107            // called.  See ConvertToLazy-1.1 in test/ConvertToLazy.tcl
108            // where we check that the moml has "configure" in it.
109            String moml = toplevel.exportMoML();
110            parser.resetAll();
111            toplevel = (TypedCompositeActor) parser.parse(moml);
112            System.out.println(toplevel.exportMoML());
113
114        } finally {
115            MoMLParser.setMoMLFilters(oldFilters);
116        }
117    }
118
119    ///////////////////////////////////////////////////////////////////
120    ////                         Public methods                    ////
121
122    /** React to a change request has been successfully executed by
123     *  doing nothing. This method is called after a change request
124     *  has been executed successfully.  In this class, we
125     *  do nothing.
126     *  @param change The change that has been executed, or null if
127     *   the change was not done via a ChangeRequest.
128     */
129    @Override
130    public void changeExecuted(ChangeRequest change) {
131    }
132
133    /** React to a change request that has resulted in an exception.
134     *  This method is called after a change request was executed,
135     *  but during the execution in an exception was thrown.
136     *  This method throws a runtime exception with a description
137     *  of the original exception.
138     *  @param change The change that was attempted or null if
139     *   the change was not done via a ChangeRequest.
140     *  @param exception The exception that resulted.
141     */
142    @Override
143    public void changeFailed(ChangeRequest change, Exception exception) {
144        // If we do not implement ChangeListener, then ChangeRequest
145        // will print any errors to stdout and continue.
146        // This causes no end of trouble with the test suite
147        // We can't throw an Exception here because this method in
148        // the base class does not throw Exception.
149        String description = "";
150
151        if (change != null) {
152            description = change.getDescription();
153        }
154
155        System.err.println("UseLazyCompositeApplication.changeFailed(): "
156                + description + " failed:\n" + exception);
157    }
158
159    /** Convert the model.
160     *  @param actor The model to convert.
161     *  @param threshold The threshold to use.
162     */
163    public void convert(TypedCompositeActor actor, int threshold) {
164        List<ComponentEntity> entities = actor.entityList();
165        for (ComponentEntity entity : entities) {
166            if (entity instanceof TypedCompositeActor) {
167                // Do the conversion depth-first.
168                convert((TypedCompositeActor) entity, threshold);
169                if (entity.getClassName()
170                        .equals("ptolemy.actor.TypedCompositeActor")
171                        && count((TypedCompositeActor) entity) >= threshold) {
172                    entity.setClassName(
173                            "ptolemy.actor.LazyTypedCompositeActor");
174                }
175            }
176        }
177        //         List<ComponentEntity> classDefinitions = actor.classDefinitionList();
178        //         for (ComponentEntity classDefinition : classDefinitions) {
179        //             if (classDefinition instanceof TypedCompositeActor) {
180        //                 // Do the conversion depth-first.
181        //                 convert((TypedCompositeActor) classDefinition, threshold);
182        //                 if (classDefinition.getClassName().equals(
183        //                         "ptolemy.actor.TypedCompositeActor")
184        //                         && count((TypedCompositeActor) classDefinition) >= threshold) {
185        //                     classDefinition
186        //                             .setClassName("ptolemy.actor.LazyTypedCompositeActor");
187        //                 }
188        //             }
189        //         }
190
191    }
192
193    /** Count the number of contained entities that have not already been made
194     *  lazy.
195     *  @param actor The actor to count.
196     *  @return The number of contained entities (deeply) that are not already
197     *   lazy.
198     */
199    public int count(TypedCompositeActor actor) {
200        int result = 0;
201        List<ComponentEntity> entities = actor.entityList();
202        for (ComponentEntity entity : entities) {
203            result++;
204            if (entity instanceof TypedCompositeActor && !entity.getClassName()
205                    .equals("ptolemy.actor.lib.LazyTypedCompositeActor")) {
206                result += count((TypedCompositeActor) entity);
207            }
208        }
209        return result;
210    }
211
212    /** Create an instance of a model and convert it.
213     *  @param args The command-line arguments providing the number
214     *   of entities threshold and naming the .xml file to convert.
215     */
216    public static void main(String[] args) {
217        try {
218            if (args.length == 0) {
219                System.err.println("Usage: FIXME  MoMLFile <numberOfEnties>\n");
220                return;
221            }
222            if (args.length == 1) {
223                new ConvertToLazy(args[0], 10);
224                return;
225            }
226            int threshold = Integer.parseInt(args[1]);
227            new ConvertToLazy(args[0], threshold);
228        } catch (Throwable ex) {
229            System.err.println("Command failed: " + ex);
230            ex.printStackTrace();
231        }
232    }
233}