001/* Dataflow utilities 002 003 Copyright (c) 2004-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.actor.util; 029 030import java.util.Comparator; 031 032import ptolemy.actor.IOPort; 033import ptolemy.actor.sched.NotSchedulableException; 034import ptolemy.data.BooleanToken; 035import ptolemy.data.IntToken; 036import ptolemy.data.Token; 037import ptolemy.data.expr.Parameter; 038import ptolemy.data.expr.TemporaryVariable; 039import ptolemy.data.expr.Variable; 040import ptolemy.kernel.Port; 041import ptolemy.kernel.util.IllegalActionException; 042import ptolemy.kernel.util.InternalErrorException; 043import ptolemy.kernel.util.KernelException; 044import ptolemy.kernel.util.NamedObj; 045import ptolemy.kernel.util.Settable; 046 047/////////////////////////////////////////////////////////////////// 048//// DFUtilities 049 050/** 051 This class factors code out of the SDF domain, for use in different 052 schedulers, so that they can be implemented in a consistent fashion. 053 This interface contains static methods that are often useful from 054 outside of an SDFDirector, and so are provided here in an interface 055 that can be imported. 056 057 @author Stephen Neuendorffer 058 @version $Id$ 059 @since Ptolemy II 4.1 060 @Pt.ProposedRating Red (neuendor) 061 @Pt.AcceptedRating Red (neuendor) 062 */ 063public class DFUtilities { 064 /////////////////////////////////////////////////////////////////// 065 //// public inner classes //// 066 067 /** A comparator for named objects. 068 */ 069 public static class NamedObjComparator implements Comparator { 070 /** Compare two objects. 071 * If the objects are not NamedObjs, then an InternalErrorException 072 * is thrown. 073 * @param object1 The first object to be compared. 074 * @param object2 The second object to be compared. 075 * @return 0 if the objects are the same. 076 */ 077 @Override 078 public int compare(Object object1, Object object2) { 079 // Note: This is rather slow, because getFullName is not cached. 080 081 if (object1 instanceof NamedObj && object2 instanceof NamedObj) { 082 // Compare full names. 083 NamedObj namedObject1 = (NamedObj) object1; 084 NamedObj namedObject2 = (NamedObj) object2; 085 int compare = namedObject1.getFullName() 086 .compareTo(namedObject2.getFullName()); 087 088 if (compare != 0) { 089 return compare; 090 } 091 092 // Compare class names. 093 Class class1 = namedObject1.getClass(); 094 Class class2 = namedObject2.getClass(); 095 compare = class1.getName().compareTo(class2.getName()); 096 097 if (compare != 0) { 098 return compare; 099 } 100 101 if (object1.equals(object2)) { 102 return 0; 103 } else { 104 // FIXME This should never happen, hopefully. Otherwise 105 // the comparator needs to be made more specific. 106 throw new InternalErrorException("Comparator not " 107 + "capable of comparing not equal objects."); 108 } 109 } else { 110 throw new InternalErrorException("Arguments to comparator " 111 + "must be instances of NamedObj: " + object1 + ", " 112 + object2); 113 } 114 } 115 } 116 117 /////////////////////////////////////////////////////////////////// 118 //// public methods //// 119 120 /** Return the number of tokens that will be produced or consumed on the 121 * given port. If the port is an input, then return its consumption 122 * rate, or if the port is an output, then return its production rate. 123 * @param port The given port. 124 * @return The number of tokens that will be produced or consumed on the 125 * given port. 126 * @exception NotSchedulableException If the port is both an input and 127 * an output, or is neither an input nor an output. 128 * @exception IllegalActionException If a rate does not contain a 129 * valid expression. 130 * @see #setRate 131 */ 132 public static int getRate(IOPort port) 133 throws NotSchedulableException, IllegalActionException { 134 if (port.isInput() && port.isOutput()) { 135 throw new NotSchedulableException(port, 136 "Port is both an input and an output, which is not" 137 + " allowed in SDF."); 138 } else if (port.isInput()) { 139 return getTokenConsumptionRate(port); 140 } else if (port.isOutput()) { 141 return getTokenProductionRate(port); 142 } else { 143 throw new NotSchedulableException(port, 144 "Port is neither an input and an output, which is not" 145 + " allowed in SDF."); 146 } 147 } 148 149 /** Get the Variable with the specified name in the given port, or 150 * with the specified name preceded by an underscore. If there 151 * is no such variable, return null. 152 * @param port The port. 153 * @param name The name of the variable. 154 * @return The variable with the specified name in the given port. 155 * @see #setRateVariable(Port, String, int) 156 */ 157 public static Variable getRateVariable(Port port, String name) { 158 Variable parameter = (Variable) port.getAttribute(name); 159 160 if (parameter == null) { 161 String altName = "_" + name; 162 parameter = (Variable) port.getAttribute(altName); 163 } 164 165 return parameter; 166 } 167 168 /** Get the integer value stored in the Variable with the 169 * specified name. If there is still no such variable, then 170 * return the specified default. 171 * @param port The port. 172 * @param name The name of the variable. 173 * @param defaultValue The default value of the variable. 174 * @return A rate. 175 * @exception IllegalActionException If the variable does not contain 176 * a valid token, or the token is not an IntToken. 177 */ 178 public static int getRateVariableValue(Port port, String name, 179 int defaultValue) throws IllegalActionException { 180 Variable parameter = getRateVariable(port, name); 181 182 if (parameter == null) { 183 return defaultValue; 184 } 185 186 Token token = parameter.getToken(); 187 188 if (token == null) { 189 // The tokenConsumptionRate parameter is present, but was 190 // not set. BooleanSelect had this problem. 191 return defaultValue; 192 } 193 194 if (token.isNil()) { 195 throw new IllegalActionException(port, 196 "Port rate parameter value is missing (is nil)."); 197 } 198 199 if (token instanceof IntToken) { 200 return ((IntToken) token).intValue(); 201 } else { 202 throw new IllegalActionException( 203 "Variable " + parameter.getFullName() + " was expected " 204 + "to contain an IntToken, but instead " 205 + "contained a " + token.getType() + "."); 206 } 207 } 208 209 /** Get the number of tokens that are consumed on the given port. 210 * If the port is not an input port, then return zero. 211 * Otherwise, return the value of the port's 212 * <i>tokenConsumptionRate</i> parameter. If this parameter does 213 * not exist, then assume the actor is homogeneous and return 214 * one. 215 * @param port The given port. 216 * @return The number of tokens the scheduler believes will be consumed 217 * from the given input port during each firing. 218 * @exception IllegalActionException If the tokenConsumptionRate 219 * parameter has an invalid expression. 220 * @see #setTokenConsumptionRate 221 */ 222 public static int getTokenConsumptionRate(IOPort port) 223 throws IllegalActionException { 224 if (!port.isInput()) { 225 return 0; 226 } else { 227 return getRateVariableValue(port, "tokenConsumptionRate", 1); 228 } 229 } 230 231 /** Get the number of tokens that are initially 232 * available on the given input port 233 * after initialization. If the port is not an 234 * input port, then presumably any initial tokens 235 * will be available on the inside. Return the value of 236 * the port's <i>tokenInitConsumption</i> parameter. If the parameter 237 * does not exist, then assume the port has no initial tokens. 238 * a value of zero. 239 * @param port The given port. 240 * @return The number of tokens the scheduler believes will be available 241 * at the given input port after initialization. 242 * @exception IllegalActionException If the tokenInitConsumption 243 * parameter has an invalid expression. 244 * @see #setTokenInitConsumption 245 */ 246 public static int getTokenInitConsumption(IOPort port) 247 throws IllegalActionException { 248 return getRateVariableValue(port, "tokenInitConsumption", 0); 249 } 250 251 /** Get the number of tokens that are produced on the given port 252 * during initialization. If the port is not an 253 * output port, then the number of tokens is presumably the number 254 * of initial tokens produced on the inside of the port. 255 * The number of tokens returned is the value of 256 * the port's <i>tokenInitProduction</i> parameter. If the parameter 257 * does not exist, then assume the actor is zero-delay and return 258 * a value of zero. 259 * @param port The given port. 260 * @return The number of tokens the scheduler believes will be produced 261 * from the given output port during initialization. 262 * @exception IllegalActionException If the tokenInitProduction 263 * parameter has an invalid expression. 264 * @see #setTokenInitProduction 265 */ 266 public static int getTokenInitProduction(IOPort port) 267 throws IllegalActionException { 268 return getRateVariableValue(port, "tokenInitProduction", 0); 269 } 270 271 /** Get the number of tokens that are produced on the given port. 272 * If the port is not an output port, then return zero. 273 * Otherwise, return the value of the port's 274 * <i>tokenProductionRate</i> parameter. If the parameter does 275 * not exist, then assume the actor is homogeneous and return a 276 * rate of one. 277 * @param port The given port. 278 * @return The number of tokens the scheduler believes will be produced 279 * from the given output port during each firing. 280 * @exception IllegalActionException If the tokenProductionRate 281 * parameter has an invalid expression. 282 * @see #setTokenProductionRate 283 */ 284 public static int getTokenProductionRate(IOPort port) 285 throws IllegalActionException { 286 if (!port.isOutput()) { 287 return 0; 288 } else { 289 return getRateVariableValue(port, "tokenProductionRate", 1); 290 } 291 } 292 293 /** If a variable with the given name does not exist, then create 294 * a variable with the given name and set the value of that 295 * variable to the specified value. The resulting variable is not 296 * persistent and not editable, but will be visible to the user. 297 * @param port The port. 298 * @param name Name of the variable. 299 * @param value The value. 300 * @exception IllegalActionException If a new parameter can not be 301 * created for the give port. 302 */ 303 public static void setExpressionIfNotDefined(Port port, String name, 304 String value) throws IllegalActionException { 305 Variable rateParameter = (Variable) port.getAttribute(name); 306 307 if (rateParameter == null) { 308 try { 309 String altName = "_" + name; 310 rateParameter = (Variable) port.getAttribute(altName); 311 312 if (rateParameter == null) { 313 rateParameter = new Parameter(port, altName); 314 rateParameter.setVisibility(Settable.NOT_EDITABLE); 315 rateParameter.setPersistent(false); 316 } 317 318 rateParameter.setExpression(value); 319 rateParameter.validate(); 320 } catch (KernelException ex) { 321 throw new InternalErrorException(port, ex, "Should not occur"); 322 } 323 } 324 } 325 326 /** If a variable with the given name does not exist, then create 327 * a variable with the given name and set the value of that 328 * variable to the specified value. The resulting variable is not 329 * persistent and not editable, but will be visible to the user. 330 * @param port The port. 331 * @param name Name of the variable. 332 * @param value The value. 333 * @exception IllegalActionException If a new parameter can not be 334 * created for the given port, or the given value is not an acceptable. 335 */ 336 public static void setIfNotDefined(Port port, String name, int value) 337 throws IllegalActionException { 338 Variable rateParameter = (Variable) port.getAttribute(name); 339 340 if (rateParameter == null) { 341 try { 342 String altName = "_" + name; 343 rateParameter = (Variable) port.getAttribute(altName); 344 345 if (rateParameter == null) { 346 rateParameter = new Parameter(port, altName); 347 rateParameter.setVisibility(Settable.NOT_EDITABLE); 348 rateParameter.setPersistent(false); 349 } 350 351 rateParameter.setToken(new IntToken(value)); 352 } catch (KernelException ex) { 353 throw new InternalErrorException(port, ex, "Should not occur"); 354 } 355 } 356 } 357 358 /** If the specified container does not contain a variable with 359 * the specified name, then create such a variable and set its 360 * value to the specified integer. The resulting variable is not 361 * persistent and not editable, but will be visible to the user. 362 * If the variable does exist, then just set its value. 363 * @param container The container. 364 * @param name Name of the variable. 365 * @param value The value. 366 * @exception IllegalActionException If the variable exists and 367 * its value cannot be set. 368 */ 369 public static void setOrCreate(NamedObj container, String name, int value) 370 throws IllegalActionException { 371 Variable variable = _getOrCreate(container, name); 372 variable.setToken(new IntToken(value)); 373 } 374 375 /** If the specified container does not contain a variable with 376 * the specified name, then create such a variable and set its 377 * expression to the specified string. The resulting variable is not 378 * persistent and not editable, but will be visible to the user. 379 * If the variable does exist, then just set its expression. 380 * @param container The container. 381 * @param name Name of the variable. 382 * @param expression The expression. 383 * @exception IllegalActionException If the variable exists and 384 * its value cannot be set. 385 */ 386 public static void setOrCreate(NamedObj container, String name, 387 String expression) throws IllegalActionException { 388 Variable variable = _getOrCreate(container, name); 389 variable.setExpression(expression); 390 } 391 392 /** Set the rate variable with the specified name to the specified 393 * value. If it doesn't exist, create it. 394 * @param port The port. 395 * @param name The variable name. 396 * @param rate The rate value. 397 * @exception IllegalActionException If the rate is a negative integer, 398 * or the rate can not be set. 399 * @return The rate parameter. 400 * @see #getRate(IOPort) 401 */ 402 public static Variable setRate(Port port, String name, int rate) 403 throws IllegalActionException { 404 if (rate < 0) { 405 throw new IllegalActionException( 406 "Negative rate is not allowed: " + rate); 407 } 408 409 Variable parameter = (Variable) port.getAttribute(name); 410 411 if (parameter != null) { 412 parameter.setToken(new IntToken(rate)); 413 } else { 414 try { 415 // Use Variable rather than Parameter so the 416 // value is transient. 417 parameter = new Variable(port, name, new IntToken(rate)); 418 parameter.setVisibility(Settable.NOT_EDITABLE); 419 parameter.setPersistent(false); 420 } catch (KernelException ex) { 421 throw new InternalErrorException(port, ex, "Should not occur"); 422 } 423 } 424 return parameter; 425 } 426 427 /** If a variable with the given name does not exist, then create 428 * a variable with the given name. Then set the value of the 429 * variable to the specified value. 430 * @param port The port. 431 * @param name Name of the variable. 432 * @param value The value. 433 * @exception IllegalActionException If a new parameter can not be 434 * created for the given port, or the given value is not an acceptable. 435 * @see #getRateVariable(Port, String) 436 */ 437 public static void setRateVariable(Port port, String name, int value) 438 throws IllegalActionException { 439 Variable rateParameter = (Variable) port.getAttribute(name); 440 441 if (rateParameter == null) { 442 try { 443 String altName = "_" + name; 444 rateParameter = (Variable) port.getAttribute(altName); 445 446 if (rateParameter == null) { 447 rateParameter = new Parameter(port, altName); 448 rateParameter.setVisibility(Settable.NOT_EDITABLE); 449 rateParameter.setPersistent(false); 450 } 451 } catch (KernelException ex) { 452 throw new InternalErrorException(port, ex, "Should not occur"); 453 } 454 } 455 rateParameter.setToken(new IntToken(value)); 456 } 457 458 /** Set the <i>tokenConsumptionRate</i> parameter of the given port 459 * to the given rate. If no parameter exists, then create a new one. 460 * The new one is an instance of Variable, so it is not persistent. 461 * That is, it will not be saved in the MoML file if the model is 462 * saved. The port is normally an input port, but this is not 463 * checked. 464 * @param port The given port. 465 * @param rate The given rate. 466 * @exception IllegalActionException If the rate is negative. 467 * @see #getTokenConsumptionRate 468 */ 469 public static void setTokenConsumptionRate(IOPort port, int rate) 470 throws IllegalActionException { 471 setRate(port, "tokenConsumptionRate", rate); 472 } 473 474 /** Set the <i>tokenInitConsumption</i> parameter of the given port to 475 * the given rate. If no parameter exists, then create a new one. 476 * The new one is an instance of Variable, so it is not persistent. 477 * That is, it will not be saved in the MoML file if the model is 478 * saved. The port is normally an output port, but this is not 479 * checked. 480 * @param port The given port. 481 * @param rate The given rate. 482 * @exception IllegalActionException If the rate is negative. 483 * @see #getTokenInitConsumption(IOPort) 484 */ 485 public static void setTokenInitConsumption(IOPort port, int rate) 486 throws IllegalActionException { 487 setRate(port, "tokenInitConsumption", rate); 488 } 489 490 /** Set the <i>tokenInitProduction</i> parameter of the given port to 491 * the given rate. If no parameter exists, then create a new one. 492 * The new one is an instance of Variable, so it is not persistent. 493 * That is, it will not be saved in the MoML file if the model is 494 * saved. The port is normally an output port, but this is not 495 * checked. 496 * @param port The given port. 497 * @param rate The given rate. 498 * @exception IllegalActionException If the rate is negative. 499 * @see #getTokenInitProduction 500 */ 501 public static void setTokenInitProduction(IOPort port, int rate) 502 throws IllegalActionException { 503 setRate(port, "tokenInitProduction", rate); 504 } 505 506 /** Set the <i>tokenProductionRate</i> parameter of the given port 507 * to the given rate. If no parameter exists, then create a new one. 508 * The new one is an instance of Variable, so it is transient. 509 * That is, it will not be exported to MoML files. 510 * The port is normally an output port, but this is not checked. 511 * @param port The given port. 512 * @param rate The given rate. 513 * @exception IllegalActionException If the rate is negative. 514 * @see #getTokenProductionRate 515 */ 516 public static void setTokenProductionRate(IOPort port, int rate) 517 throws IllegalActionException { 518 setRate(port, "tokenProductionRate", rate); 519 } 520 521 /** Depending on the given flag, add an invisible, persistent 522 * variable named "_showRate" with value true to the given port 523 * that indicates to the user interface that rate parameters on 524 * the given port should be displayed in the user interface. 525 * @param port The port. 526 * @param flag The flag. 527 * @exception IllegalActionException If a new parameter can not be 528 * created for the given port, or the given flag is not an acceptable. 529 */ 530 public static void showRate(Port port, boolean flag) 531 throws IllegalActionException { 532 String name = "_showRate"; 533 534 // Look for an existing parameter. 535 Variable variable = (Variable) port.getAttribute(name); 536 537 if (variable == null) { 538 try { 539 variable = new Parameter(port, name); 540 variable.setVisibility(Settable.EXPERT); 541 variable.setPersistent(false); 542 } catch (KernelException ex) { 543 throw new InternalErrorException(port, ex, "Should not occur"); 544 } 545 } 546 547 variable.setToken(BooleanToken.getInstance(flag)); 548 } 549 550 /////////////////////////////////////////////////////////////////// 551 //// private methods //// 552 // If a variable exists with the given container and given name, 553 // then return it. Otherwise, create the variable and return it. 554 private static Variable _getOrCreate(NamedObj container, String name) { 555 Variable variable = (Variable) container.getAttribute(name); 556 557 if (variable == null) { 558 try { 559 variable = new TemporaryVariable(container, name); 560 variable.setVisibility(Settable.NOT_EDITABLE); 561 variable.setPersistent(false); 562 } catch (KernelException ex) { 563 throw new InternalErrorException(container, ex, 564 "Should not occur"); 565 } 566 } 567 568 return variable; 569 } 570}