001/* A Ptolemy application that instantiates class names given on the command 002 line. 003 004 Copyright (c) 2004-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.actor.gui; 030 031import java.util.Iterator; 032import java.util.LinkedList; 033import java.util.List; 034 035import ptolemy.actor.CompositeActor; 036import ptolemy.actor.Director; 037import ptolemy.actor.Manager; 038import ptolemy.actor.injection.PortablePlaceable; 039import ptolemy.data.expr.Variable; 040import ptolemy.kernel.attributes.VersionAttribute; 041import ptolemy.kernel.util.Attribute; 042import ptolemy.kernel.util.IllegalActionException; 043import ptolemy.kernel.util.KernelException; 044import ptolemy.moml.MoMLParser; 045import ptolemy.util.StringUtilities; 046 047/////////////////////////////////////////////////////////////////// 048//// CompositeActorSimpleApplication 049 050/** 051 This application creates one or more Ptolemy II models given a 052 classname on the command line, and then executes those models, each in 053 its own thread. Each specified class should be derived from 054 CompositeActor, and should have a constructor that takes a single 055 argument, an instance of Workspace. If the model does not contain 056 a manager, then one will be created for it. 057 <p> 058 The model is not displayed, models that have actors that extend 059 Placeable should instead use 060 {@link ptolemy.actor.gui.CompositeActorApplication}. 061 </p> 062 <p> 063 The command-line arguments can also set parameter values for any 064 parameter in the models, with the name given relative to the top-level 065 entity. For example, to specify the iteration count in an SDF model, 066 you can invoke this on the command line as follows: 067 </p> 068 <pre> 069 java -classpath $PTII ptolemy.actor.gui.CompositeActorSimpleApplication \ 070 -director.iterations 1000 \ 071 -class ptolemy.actor.gui.test.TestModel 072 </pre> 073 <p> 074 This assumes that the model given by the specified class name has a director 075 named "director" with a parameter named "iterations". If more than 076 one model is given on the command line, then the parameter values will 077 be set for all models that have such a parameter. 078 </p> 079 080 @see ptolemy.actor.gui.CompositeActorApplication 081 @author Christopher Brooks 082 @version $Id$ 083 @since Ptolemy II 4.1 084 @Pt.ProposedRating Red (cxh) 085 @Pt.AcceptedRating Red (vogel) 086 */ 087public class CompositeActorSimpleApplication { 088 /////////////////////////////////////////////////////////////////// 089 //// public methods //// 090 091 /** Create a new application with the specified command-line arguments. 092 * @param args The command-line arguments. 093 */ 094 public static void main(String[] args) { 095 CompositeActorSimpleApplication application = new CompositeActorSimpleApplication(); 096 _run(application, args); 097 } 098 099 /** Return the list of models. 100 * @return The list of models passed in as arguments. 101 */ 102 public List<CompositeActor> models() { 103 // Used primarily for testing. 104 return _models; 105 } 106 107 /** Parse the command-line arguments, creating models as specified. 108 * @param args The command-line arguments. 109 * @exception Exception If something goes wrong. 110 */ 111 public void processArgs(String[] args) throws Exception { 112 if (args != null) { 113 _parseArgs(args); 114 115 // start the models. 116 Iterator models = _models.iterator(); 117 118 while (models.hasNext()) { 119 startRun((CompositeActor) models.next()); 120 } 121 } 122 } 123 124 /** Report an exception. This prints a message to the standard error 125 * stream, followed by the stack trace. 126 * @param ex The exception to report. 127 */ 128 public void report(Exception ex) { 129 report("", ex); 130 } 131 132 /** Report a message to the user. 133 * This prints a message to the standard output stream. 134 * @param message The message to report. 135 */ 136 public void report(String message) { 137 System.out.println(message); 138 } 139 140 /** Report an exception with an additional message. 141 * This prints a message to standard error, followed by the 142 * stack trace. 143 * @param message The message. 144 * @param ex The exception to report. 145 */ 146 public void report(String message, Exception ex) { 147 System.err.println("Exception thrown:\n" + message + "\n" 148 + KernelException.stackTraceToString(ex)); 149 } 150 151 /** If the specified model has a manager and is not already running, 152 * then execute the model in a new thread. Otherwise, do nothing. 153 * If the model contains an atomic entity that implements Placeable, 154 * we create create an instance of ModelFrame, if nothing implements 155 * Placeable, then we do not create an instance of ModelFrame. This 156 * allows us to run non-graphical models on systems that do not have 157 * a display. 158 * <p> 159 * We then start the model running. 160 * 161 * @param model The model to execute. 162 * @exception IllegalActionException If the model contains Placeables. 163 * or does not have a manager. 164 * @return Always returns null. 165 * @see ptolemy.actor.Manager#startRun() 166 */ 167 public synchronized Object startRun(CompositeActor model) 168 throws IllegalActionException { 169 // This method is synchronized so that it can atomically modify 170 // the count of executing processes. 171 // NOTE: If you modify this method, please be sure that it 172 // will work for non-graphical models in the nightly test suite. 173 // Iterate through the model, looking for something that is Placeable. 174 Iterator atomicEntities = model.allAtomicEntityList().iterator(); 175 176 while (atomicEntities.hasNext()) { 177 Object object = atomicEntities.next(); 178 179 if (object instanceof Placeable 180 || object instanceof PortablePlaceable) { 181 throw new IllegalActionException( 182 "CompositeActorSimpleApplication does not support " 183 + "actors that are instances of placeable, " 184 + "object was: " + object); 185 } 186 } 187 188 Manager manager = model.getManager(); 189 190 if (manager != null) { 191 try { 192 manager.startRun(); 193 } catch (IllegalActionException ex) { 194 // Model is already running. Ignore. 195 } 196 } else { 197 report("Model " + model.getFullName() + " cannot be executed " 198 + "because it does not have a manager."); 199 } 200 return null; 201 } 202 203 /** If the specified model has a manager and is executing, then 204 * stop execution by calling the stop() method of the manager. 205 * If there is no manager, do nothing. 206 * @param model The model to stop. 207 */ 208 public void stopRun(CompositeActor model) { 209 Manager manager = model.getManager(); 210 211 if (manager != null) { 212 manager.stop(); 213 } 214 } 215 216 /** Wait for all windows to close. 217 */ 218 public synchronized void waitForFinish() { 219 while (_openCount > 0) { 220 try { 221 wait(); 222 } catch (InterruptedException ex) { 223 break; 224 } 225 } 226 } 227 228 /////////////////////////////////////////////////////////////////// 229 //// protected methods //// 230 231 /** Parse a command-line argument. The recognized arguments, which 232 * result in this method returning true, are summarized below: 233 * <ul> 234 * <li>If the argument is "-class", then attempt to interpret 235 * the next argument as the fully qualified classname of a class 236 * to instantiate as a ptolemy model. The model will be created, 237 * added to the directory of models, and then executed. 238 * <li>If the argument is "-help", then print a help message. 239 * <li>If the argument is "-test", then set a flag that will 240 * abort execution of any created models after two seconds. 241 * <li>If the argument is "-version", then print a short version message. 242 * <li>If the argument is "", then ignore it. 243 * </ul> 244 * Otherwise, the argument is ignored and false is returned. 245 * 246 * @param arg The argument to be parse. 247 * @return True if the argument is understood, false otherwise. 248 * @exception Exception If something goes wrong. 249 */ 250 protected boolean _parseArg(String arg) throws Exception { 251 if (arg.equals("-class")) { 252 _expectingClass = true; 253 } else if (arg.equals("-help")) { 254 System.out.println(_usage()); 255 256 // Don't call System.exit(0) here, it will break the test suites 257 } else if (arg.equals("-test")) { 258 _test = true; 259 } else if (arg.equals("-version")) { 260 System.out.println("Version " + VersionAttribute.CURRENT_VERSION.getExpression()); 261 262 // quit the program if the user asked for the version 263 // Don't call System.exit(0) here, it will break the test suites 264 } else if (arg.equals("")) { 265 // Ignore blank argument. 266 } else { 267 if (_expectingClass) { 268 _expectingClass = false; 269 270 MoMLParser parser = new MoMLParser(); 271 String string = "<entity name=\"toplevel\" class=\"" + arg 272 + "\"/>"; 273 CompositeActor model = (CompositeActor) parser.parse(string); 274 275 // Temporary hack because cloning doesn't properly clone 276 // type constraints. 277 CompositeActor modelClass = (CompositeActor) parser 278 .searchForClass(arg, model.getSource()); 279 280 if (modelClass != null) { 281 model = modelClass; 282 } 283 284 _models.add(model); 285 286 // Create a manager. 287 Manager manager = model.getManager(); 288 289 if (manager == null) { 290 model.setManager(new Manager(model.workspace(), "manager")); 291 //manager = model.getManager(); 292 } 293 } else { 294 // Argument not recognized. 295 return false; 296 } 297 } 298 299 return true; 300 } 301 302 /** Parse the command-line arguments. 303 * @param args The arguments to be parsed. 304 * @exception Exception If an argument is not understood or triggers 305 * an error. 306 */ 307 protected void _parseArgs(String[] args) throws Exception { 308 for (int i = 0; i < args.length; i++) { 309 String arg = args[i]; 310 311 if (_parseArg(arg) == false) { 312 if (arg.startsWith("-") && i < args.length - 1) { 313 // Save in case this is a parameter name and value. 314 _parameterNames.add(arg.substring(1)); 315 _parameterValues.add(args[i + 1]); 316 i++; 317 } else { 318 // Unrecognized option. 319 throw new IllegalActionException( 320 "Unrecognized option: " + arg); 321 } 322 } 323 } 324 325 if (_expectingClass) { 326 throw new IllegalActionException("Missing classname."); 327 } 328 329 // Check saved options to see whether any is a parameter. 330 Iterator names = _parameterNames.iterator(); 331 Iterator values = _parameterValues.iterator(); 332 333 while (names.hasNext() && values.hasNext()) { 334 String name = (String) names.next(); 335 String value = (String) values.next(); 336 337 boolean match = false; 338 Iterator models = _models.iterator(); 339 340 while (models.hasNext()) { 341 CompositeActor model = (CompositeActor) models.next(); 342 Attribute attribute = model.getAttribute(name); 343 344 if (attribute instanceof Variable) { 345 match = true; 346 ((Variable) attribute).setExpression(value); 347 348 // Force evaluation so that listeners are notified. 349 ((Variable) attribute).getToken(); 350 } 351 352 Director director = model.getDirector(); 353 354 if (director != null) { 355 attribute = director.getAttribute(name); 356 357 if (attribute instanceof Variable) { 358 match = true; 359 ((Variable) attribute).setExpression(value); 360 361 // Force evaluation so that listeners are notified. 362 ((Variable) attribute).getToken(); 363 } 364 } 365 } 366 367 if (!match) { 368 // Unrecognized option. 369 throw new IllegalActionException( 370 "Unrecognized option: " + "-" + name); 371 } 372 } 373 } 374 375 /** Run the application. 376 * @param application The application. 377 * @param args The arguments to be passed to the application. 378 */ 379 protected static void _run(CompositeActorSimpleApplication application, 380 String[] args) { 381 try { 382 application.processArgs(args); 383 application.waitForFinish(); 384 } catch (Exception ex) { 385 System.err.println(KernelException.stackTraceToString(ex)); 386 StringUtilities.exit(0); 387 } 388 389 // If the -test arg was set, then exit after 2 seconds. 390 if (_test) { 391 try { 392 Thread.sleep(2000); 393 } catch (InterruptedException e) { 394 } 395 396 StringUtilities.exit(0); 397 } 398 } 399 400 /** Return a string summarizing the command-line arguments. 401 * @return A usage string. 402 */ 403 protected String _usage() { 404 StringBuffer result = new StringBuffer("Usage: " + _commandTemplate 405 + "\n\n" + "Options that take values:\n"); 406 407 int i; 408 409 for (i = 0; i < _commandOptions.length; i++) { 410 result.append(" " + _commandOptions[i][0] + " " 411 + _commandOptions[i][1] + "\n"); 412 } 413 414 result.append("\nBoolean flags:\n"); 415 416 for (i = 0; i < _commandFlags.length; i++) { 417 result.append(" " + _commandFlags[i]); 418 } 419 420 return result.toString(); 421 } 422 423 /////////////////////////////////////////////////////////////////// 424 //// protected variables //// 425 426 /** The command-line options that are either present or not. */ 427 protected String[] _commandFlags = { "-help", "-test", "-version", }; 428 429 /** The command-line options that take arguments. */ 430 protected String[][] _commandOptions = { { "-class", "<classname>" }, 431 { "-<parameter name>", "<parameter value>" }, }; 432 433 /** The form of the command line. */ 434 protected String _commandTemplate = "ptolemy [ options ]"; 435 436 /** The list of all the models. */ 437 protected List<CompositeActor> _models = new LinkedList<CompositeActor>(); 438 439 /** The count of currently open windows. */ 440 protected int _openCount = 0; 441 442 /** If true, then auto exit after a few seconds. */ 443 protected static boolean _test = false; 444 445 /////////////////////////////////////////////////////////////////// 446 //// private variables //// 447 // Flag indicating that the previous argument was -class. 448 private boolean _expectingClass = false; 449 450 // List of parameter names seen on the command line. 451 private List _parameterNames = new LinkedList(); 452 453 // List of parameter values seen on the command line. 454 private List _parameterValues = new LinkedList(); 455}