001/* Base class for plotters. 002 003 @Copyright (c) 1998-2014 The Regents of the University of California. 004 All rights reserved. 005 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 009 above copyright notice and the following two paragraphs appear in all 010 copies 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 */ 028package ptolemy.actor.lib.gui; 029 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.PrintWriter; 033import java.io.StringWriter; 034import java.io.Writer; 035import java.net.URL; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.StringTokenizer; 040 041import ptolemy.actor.TypedAtomicActor; 042import ptolemy.actor.injection.ActorModuleInitializer; 043import ptolemy.actor.injection.PortableContainer; 044import ptolemy.actor.injection.PortablePlaceable; 045import ptolemy.actor.injection.PtolemyInjector; 046import ptolemy.data.BooleanToken; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.type.BaseType; 049import ptolemy.kernel.CompositeEntity; 050import ptolemy.kernel.util.Attribute; 051import ptolemy.kernel.util.Configurable; 052import ptolemy.kernel.util.IllegalActionException; 053import ptolemy.kernel.util.NameDuplicationException; 054import ptolemy.kernel.util.Nameable; 055import ptolemy.kernel.util.NamedObj; 056import ptolemy.kernel.util.StringAttribute; 057import ptolemy.kernel.util.Workspace; 058import ptolemy.plot.PlotBoxInterface; 059import ptolemy.plot.PlotInterface; 060import ptolemy.plot.plotml.PlotMLParser; 061 062/////////////////////////////////////////////////////////////////// 063//// PlotterBase 064 065/** 066 Base class for plotters. This class contains an instance of the 067 PlotBox class from the Ptolemy plot package as a public member, 068 although which subclass of PlotBox is created is left to derived classes. 069 It provides a parameter that determines whether to fill the plot 070 when wrapup is invoked. It also has a <i>legend</i> parameter, 071 which gives a comma-separated list of labels to attach to 072 each dataset. Normally, the number of elements in this list 073 should equal the number of input channels, although this 074 is not enforced. 075 076 @see ptolemy.plot.PlotBox 077 078 @author Edward A. Lee 079 @version $Id$ 080 @since Ptolemy II 2.1 081 @Pt.ProposedRating Green (eal) 082 @Pt.AcceptedRating Green (cxh) 083 */ 084public class PlotterBase extends TypedAtomicActor 085 implements Configurable, PortablePlaceable { 086 /** Construct an actor with the given container and name. 087 * @param container The container. 088 * @param name The name of this actor. 089 * @exception IllegalActionException If the actor cannot be contained 090 * by the proposed container. 091 * @exception NameDuplicationException If the container already has an 092 * actor with this name. 093 */ 094 public PlotterBase(CompositeEntity container, String name) 095 throws IllegalActionException, NameDuplicationException { 096 super(container, name); 097 098 fillOnWrapup = new Parameter(this, "fillOnWrapup", 099 new BooleanToken(true)); 100 fillOnWrapup.setTypeEquals(BaseType.BOOLEAN); 101 102 automaticRescale = new Parameter(this, "automaticRescale", 103 new BooleanToken(false)); 104 automaticRescale.setTypeEquals(BaseType.BOOLEAN); 105 106 legend = new StringAttribute(this, "legend"); 107 108 _getImplementation().initWindowAndSizeProperties(); 109 110 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"-20\" y=\"-20\" " 111 + "width=\"40\" height=\"40\" " + "style=\"fill:lightGrey\"/>\n" 112 + "<rect x=\"-12\" y=\"-12\" " + "width=\"24\" height=\"24\" " 113 + "style=\"fill:white\"/>\n" + "<rect x=\"2\" y=\"-18\" " 114 + "width=\"4\" height=\"4\" " + "style=\"fill:grey\"/>\n" 115 + "<rect x=\"8\" y=\"-18\" " + "width=\"4\" height=\"4\" " 116 + "style=\"fill:grey\"/>\n" + "<rect x=\"14\" y=\"-18\" " 117 + "width=\"4\" height=\"4\" " + "style=\"fill:grey\"/>\n" 118 + "<polyline points=\"-10,0, -5,-8, 5,8, 10,0\" " 119 + "style=\"stroke:red\"/>\n" + "</svg>\n"); 120 } 121 122 /////////////////////////////////////////////////////////////////// 123 //// ports and parameters //// 124 125 /** If true, the plot will automatically rescale if necessary. 126 * This parameter has type BooleanToken, and default value false. 127 */ 128 public Parameter automaticRescale; 129 130 /** The plot object. */ 131 public transient PlotBoxInterface plot; 132 133 /** If true, fill the plot when wrapup is called. 134 * This parameter has type BooleanToken, and default value true. 135 */ 136 public Parameter fillOnWrapup; 137 138 /** A comma-separated list of labels to attach to each data set. 139 * This is always a string, with no enclosing quotation marks. 140 */ 141 public StringAttribute legend; 142 143 /////////////////////////////////////////////////////////////////// 144 //// public methods //// 145 146 /** If the attribute is <i>legend</i>, then parse the string 147 * and set the legend. 148 * @param attribute The attribute that changed. 149 * @exception IllegalActionException If the superclass throws it. 150 */ 151 @Override 152 public void attributeChanged(Attribute attribute) 153 throws IllegalActionException { 154 // NOTE: Do not react to changes in _windowProperties. 155 // Those properties are only used when originally opening a window. 156 if (attribute == legend) { 157 if (plot != null) { 158 plot.clearLegends(); 159 160 String value = legend.getExpression(); 161 162 if (value != null && !value.trim().equals("")) { 163 StringTokenizer tokenizer = new StringTokenizer(value, ","); 164 int channel = 0; 165 166 while (tokenizer.hasMoreTokens()) { 167 plot.addLegend(channel++, tokenizer.nextToken().trim()); 168 } 169 } 170 } 171 } else { 172 super.attributeChanged(attribute); 173 } 174 } 175 176 /** Free up memory when closing. */ 177 public void cleanUp() { 178 setFrame(null); 179 _getImplementation().cleanUp(); 180 } 181 182 /** Clone the actor into the specified workspace. This calls the 183 * base class and then creates new ports and parameters. 184 * @param workspace The workspace for the new object. 185 * @return A new actor. 186 * @exception CloneNotSupportedException If a derived class has an 187 * attribute that cannot be cloned. 188 */ 189 @Override 190 public Object clone(Workspace workspace) throws CloneNotSupportedException { 191 PlotterBase newObject = (PlotterBase) super.clone(workspace); 192 193 newObject._configureBases = null; 194 newObject._configureSources = null; 195 newObject._configureTexts = null; 196 newObject._implementation = null; 197 newObject.plot = null; 198 try { 199 if (_base != null) { 200 newObject._base = new URL(_base.toString()); 201 } 202 newObject.configure(newObject._base, _source, _text); 203 204 // See _getImplementation(): 205 if (PtolemyInjector.getInjector() == null) { 206 System.err.println("Warning: main() did not call " 207 + "ActorModuleInitializer.initializeInjector(), " 208 + "so PlotterBase.clone() is calling it for you."); 209 ActorModuleInitializer.initializeInjector(); 210 } 211 newObject._implementation = PtolemyInjector.getInjector() 212 .getInstance(PlotterBaseInterface.class); 213 newObject._implementation.init(newObject); 214 215 newObject._implementation.initWindowAndSizeProperties(); 216 217 } catch (Exception e) { 218 // This should not occur. 219 throw new CloneNotSupportedException("Clone failed: " + e); 220 } 221 return newObject; 222 } 223 224 /** Configure the object with data from the specified input source 225 * (a URL) and/or textual data, assumed to be in PlotML format. 226 * If this is called before the plotter has been created 227 * (by calling place() or initialize()), then the configuration 228 * is deferred until the plotter is created. 229 * @param base The base relative to which references within the input 230 * are found, or null if this is not known, or there is none. 231 * @param source The input source, which specifies a URL. 232 * @param text Configuration information given as text. 233 * @exception Exception If the configuration source cannot be read 234 * or if the configuration information is incorrect. 235 */ 236 @Override 237 public void configure(URL base, String source, String text) 238 throws Exception { 239 _base = base; 240 _source = source; 241 _text = text; 242 243 if (plot instanceof PlotInterface) { 244 PlotMLParser parser = new PlotMLParser((PlotInterface) plot); 245 246 if (source != null && !source.trim().equals("")) { 247 URL xmlFile = new URL(base, source); 248 InputStream stream = xmlFile.openStream(); 249 parser.parse(base, stream); 250 stream.close(); 251 _configureSource = source; 252 } 253 254 // Avoid trying to parse whitespace only text. 255 if (text != null) { 256 String trimmed = text.trim(); 257 if (trimmed != null && !trimmed.equals("")) { 258 // NOTE: Regrettably, the XML parser we are using cannot 259 // deal with having a single processing instruction at the 260 // outer level. Thus, we have to strip it. 261 262 if (trimmed.startsWith("<?") && trimmed.endsWith("?>")) { 263 trimmed = trimmed.substring(2, trimmed.length() - 2) 264 .trim(); 265 266 if (trimmed.startsWith("plotml")) { 267 trimmed = trimmed.substring(6).trim(); 268 parser.parse(base, trimmed); 269 } 270 271 // If it's not a plotml processing instruction, ignore. 272 } else { 273 // Data is not enclosed in a processing instruction. 274 // Must have been given in a CDATA section. 275 parser.parse(base, trimmed); 276 } 277 } 278 } 279 } else { 280 // Defer until plot has been placed. 281 if (_configureBases == null) { 282 _configureBases = new LinkedList<URL>(); 283 _configureSources = new LinkedList<String>(); 284 _configureTexts = new LinkedList<String>(); 285 } 286 287 _configureBases.add(base); 288 _configureSources.add(source); 289 _configureTexts.add(text); 290 } 291 } 292 293 /** Return the input source that was specified the last time the configure 294 * method was called. 295 * @return The string representation of the input URL. 296 */ 297 @Override 298 public String getConfigureSource() { 299 return _configureSource; 300 } 301 302 /** Return the text string that represents the current configuration of 303 * this object. Note that any configuration that was previously 304 * specified using the source attribute need not be returned here. 305 * This returns a null string if there is no associated plot. 306 * @return The text string that represent the current configuration. 307 */ 308 @Override 309 public String getConfigureText() { 310 if (plot == null) { 311 // NOTE: Is this the right thing to do? 312 return ""; 313 } else { 314 // NOTE: Cannot include xml spec in the header because processing 315 // instructions cannot be nested in XML (lame, isn't it?). 316 //String header 317 // = "<!DOCTYPE plot PUBLIC \"-//UC Berkeley//DTD PlotML 1//EN\"\n" + "\"http://ptolemy.eecs.berkeley.edu/xml/dtd/PlotML_1.dtd\">"; 318 StringWriter writer = new StringWriter(); 319 PrintWriter print = new PrintWriter(writer); 320 321 // NOTE: Cannot include xml spec in the header because processing 322 // instructions cannot be nested in XML (lame, isn't it?). 323 //print.write(header); 324 print.write("\n<plot>\n"); 325 326 // The second (null) argument indicates that PlotML PUBLIC DTD 327 // should be referenced. 328 plot.writeFormat(print); 329 print.write("</plot>\n"); 330 return writer.toString(); 331 } 332 } 333 334 /** Specify the container into which this plot should be placed. 335 * This method needs to be called before the first call to initialize(). 336 * Otherwise, the plot will be placed in its own frame. 337 * The size of the plot, unfortunately, cannot be effectively 338 * determined from the size of the container because the 339 * container may not yet be laid out (its size will be zero). 340 * Thus, you will have to explicitly 341 * set the size of the plot by calling plot.setSize(). 342 * The background of the plot is set equal to that of the container 343 * (unless it is null). 344 * <p> 345 * If configure() has been called (prior to the plot getting created), 346 * then the configurations that it specified have been deferred. Those 347 * configurations are performed at this time. 348 * 349 * @param container The container into which to place the plot, or 350 * null to specify that a new plot should be created. 351 */ 352 @Override 353 public void place(PortableContainer container) { 354 _getImplementation().setPlatformContainer( 355 container != null ? container.getPlatformContainer() : null); 356 _getImplementation().removeNullContainer(); 357 358 if (container != null) { 359 if (container.getPlatformContainer() instanceof PlotBoxInterface) { 360 // According to FindBugs the cast is an error: 361 // [M D BC] Unchecked/unconfirmed cast [BC_UNCONFIRMED_CAST] 362 // However it is checked that _container instanceof PlotBox, 363 // so FindBugs is wrong. 364 plot = (PlotBoxInterface) container.getPlatformContainer(); 365 plot.setButtons(true); 366 } else { 367 if (plot == null) { 368 plot = _newPlot(); 369 plot.setTitle(getName()); 370 } 371 372 plot.setButtons(true); 373 container.add(plot); 374 // java.awt.Component.setBackground(color) says that 375 // if the color "parameter is null then this component 376 // will inherit the background color of its parent." 377 plot.setBackground(null); 378 } 379 380 // If the container is non-null and configurations have 381 // been deferred, implement them now. 382 // There was a bug here where we got a ConcurrentModificationException 383 // because ModelPane.setModel() called ModelPane._closeDisplays(), 384 // which called PlotterBase.place(null) and then _implementDeferredConfigurations 385 // iterated through the list of bases while it was being modified. 386 // To replicate, do open Spectrum.xml and do View -> Run Window. 387 _implementDeferredConfigurations(); 388 } 389 } 390 391 /** Clear the plot, if there is one. Notice that unlike 392 * initialize(), this clears the entire plot. 393 * @see #initialize() 394 * @exception IllegalActionException If the parent class throws it. 395 */ 396 @Override 397 public void preinitialize() throws IllegalActionException { 398 super.preinitialize(); 399 400 if (plot != null) { 401 // Do not clear the axes. 402 plot.clear(false); 403 plot.repaint(); 404 405 if (((BooleanToken) automaticRescale.getToken()).booleanValue()) { 406 plot.setAutomaticRescale(true); 407 } 408 } 409 410 } 411 412 /** Override the base class to remove the plot from its graphical 413 * container if the argument is null. 414 * @param container The proposed container. 415 * @exception IllegalActionException If the base class throws it. 416 * @exception NameDuplicationException If the base class throws it. 417 */ 418 @Override 419 public void setContainer(CompositeEntity container) 420 throws IllegalActionException, NameDuplicationException { 421 Nameable previousContainer = getContainer(); 422 super.setContainer(container); 423 424 if (container != previousContainer && previousContainer != null) { 425 _remove(); 426 } 427 } 428 429 /** Specify the associated frame and set its properties (size, etc.) 430 * to match those stored in the _windowProperties attribute. 431 * @param frame The associated frame. 432 */ 433 public void setFrame(Object frame) { 434 _getImplementation().setFrame(frame); 435 } 436 437 /** Set a name to present to the user. 438 * <p>If the Plot window has been rendered, then the title of the 439 * Plot window will be updated to the value of the name parameter.</p> 440 * @param name A name to present to the user. 441 * @see #getDisplayName() 442 */ 443 @Override 444 public void setDisplayName(String name) { 445 super.setDisplayName(name); 446 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4302 447 _getImplementation().setTableauTitle(name); 448 } 449 450 /** Set or change the name. If a null argument is given the 451 * name is set to an empty string. 452 * Increment the version of the workspace. 453 * This method is write-synchronized on the workspace. 454 * <p>If the Plot window has been rendered, then the title of the 455 * Plot window will be updated to the value of the name parameter.</p> 456 * @param name The new name. 457 * @exception IllegalActionException If the name contains a period 458 * or if the object is a derived object and the name argument does 459 * not match the current name. 460 * @exception NameDuplicationException Not thrown in this base class. 461 * May be thrown by derived classes if the container already contains 462 * an object with this name. 463 * @see #getName() 464 * @see #getName(NamedObj) 465 */ 466 @Override 467 public void setName(String name) 468 throws IllegalActionException, NameDuplicationException { 469 super.setName(name); 470 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4302 471 _getImplementation().setTableauTitle(name); 472 } 473 474 /** If the <i>fillOnWrapup</i> parameter is true, rescale the 475 * plot so that all the data is visible. 476 * @exception IllegalActionException If the superclass throws it. 477 */ 478 @Override 479 public void wrapup() throws IllegalActionException { 480 if (((BooleanToken) fillOnWrapup.getToken()).booleanValue()) { 481 if (plot != null) { 482 plot.fillPlot(); 483 } 484 } 485 486 if (plot != null) { 487 // If we are generating code, then plot might be null; 488 plot.setAutomaticRescale(false); 489 } 490 491 super.wrapup(); 492 } 493 494 /////////////////////////////////////////////////////////////////// 495 //// protected methods //// 496 497 /** Write a MoML description of the contents of this object, which 498 * in this class is the configuration information. This method is called 499 * by exportMoML(). Each description is indented according to the 500 * specified depth and terminated with a newline character. 501 * @param output The output stream to write to. 502 * @param depth The depth in the hierarchy, to determine indenting. 503 * @exception IOException If an I/O error occurs. 504 */ 505 @Override 506 protected void _exportMoMLContents(Writer output, int depth) 507 throws IOException { 508 // Make sure that the current position of the frame, if any, 509 // is up to date. 510 _getImplementation().updateWindowAndSizeAttributes(); 511 super._exportMoMLContents(output, depth); 512 513 // NOTE: Cannot include xml spec in the header because processing 514 // instructions cannot be nested in XML (lame, isn't it?). 515 String header = "<!DOCTYPE plot PUBLIC \"-//UC Berkeley//DTD PlotML 1//EN\"\n" 516 + "\"http://ptolemy.eecs.berkeley.edu/xml/dtd/PlotML_1.dtd\">"; 517 518 if (plot != null) { 519 output.write(_getIndentPrefix(depth) + "<configure>\n<?plotml " 520 + header + "\n<plot>\n"); 521 522 PrintWriter print = new PrintWriter(output); 523 524 // The second (null) argument indicates that PlotML PUBLIC DTD 525 // should be referenced. 526 plot.writeFormat(print); 527 output.write( 528 "</plot>?>\n" + _getIndentPrefix(depth) + "</configure>\n"); 529 } else if (_configureSources != null) { 530 // Configuration has been specified, but not yet evaluated. 531 // Save the configuration just as specified. 532 // Note that the bases are lost, since those are presumably 533 // the URL of the file containing the XML. 534 Iterator<String> sources = _configureSources.iterator(); 535 Iterator<String> texts = _configureTexts.iterator(); 536 537 while (sources.hasNext()) { 538 String source = sources.next(); 539 String text = texts.next(); 540 541 if (source != null && !source.trim().equals("")) { 542 output.write(_getIndentPrefix(depth) 543 + "<configure source=\"" + source + "\">"); 544 545 if (text != null) { 546 output.write("<![CDATA[\n"); 547 } 548 } else { 549 output.write(_getIndentPrefix(depth) + "<configure>\n"); 550 } 551 552 if (text != null) { 553 output.write(text.trim() + "\n"); 554 555 if (source != null && !source.trim().equals("")) { 556 output.write(_getIndentPrefix(depth) + "]]>\n"); 557 } 558 } 559 560 output.write(_getIndentPrefix(depth) + "</configure>\n"); 561 } 562 } 563 } 564 565 /** Get the right instance of the implementation depending upon the 566 * of the dependency specified through dependency injection. 567 * If the instance has not been created, then it is created. 568 * If the instance already exists then return the same. 569 * 570 * <p>This code is used as part of the dependency injection needed for the 571 * HandSimDroid project, see $PTII/ptserver. This code uses dependency 572 * inject to determine what implementation to use at runtime. 573 * This method eventually reads ptolemy/actor/ActorModule.properties. 574 * {@link ptolemy.actor.injection.ActorModuleInitializer#initializeInjector()} 575 * should be called before this method is called. If it is not 576 * called, then a message is printed and initializeInjector() is called.</p> 577 * @return the implementation. 578 */ 579 protected PlotterBaseInterface _getImplementation() { 580 if (_implementation == null) { 581 if (PtolemyInjector.getInjector() == null) { 582 System.err.println("Warning: main() did not call " 583 + "ActorModuleInitializer.initializeInjector(), " 584 + "so PlotterBase is calling it for you."); 585 ActorModuleInitializer.initializeInjector(); 586 } 587 _implementation = PtolemyInjector.getInjector() 588 .getInstance(PlotterBaseInterface.class); 589 _implementation.init(this); 590 } 591 return _implementation; 592 } 593 594 /** If configurations have been deferred, implement them now. 595 * Also, configure the plot legends, if appropriate. 596 */ 597 protected void _implementDeferredConfigurations() { 598 if (_configureSources != null) { 599 600 // Coverity indicates that configure() can modify 601 // _configureSources, _configureTexts and _configureBases, 602 // which would invalidate the iterators because the 603 // underlying list would be modified. However, the logic 604 // of this program is such that configure() would only add 605 // to these lists if plot was not yet a PlotInterface. 606 // However, place() sets plot to a plotter and then calls 607 // this method, so logically, the underlying lists cannot 608 // be moderated. However, derived classes could do 609 // something different, so copying the lists is a good 610 // idea. 611 612 Iterator<String> sources = new LinkedList<String>(_configureSources) 613 .iterator(); 614 Iterator<String> texts = new LinkedList<String>(_configureTexts) 615 .iterator(); 616 Iterator<URL> bases = new LinkedList<URL>(_configureBases) 617 .iterator(); 618 619 while (sources.hasNext()) { 620 URL base = bases.next(); 621 String source = sources.next(); 622 String text = texts.next(); 623 try { 624 configure(base, source, text); 625 } catch (Exception ex) { 626 System.out.println( 627 "Failed to parse? base: \"" + base + "\": source:\"" 628 + source + "text:\"" + text + "\""); 629 getManager().notifyListenersOfException(ex); 630 } 631 } 632 633 _configureSources = null; 634 _configureTexts = null; 635 _configureBases = null; 636 } 637 638 // Configure the new plot with legends, if appropriate. 639 try { 640 attributeChanged(legend); 641 } catch (IllegalActionException ex) { 642 // Safe to ignore because user would 643 // have already been alerted. 644 } 645 } 646 647 /** Override the base class to ensure that MoML is produced 648 * if there is configuration information to export. 649 * @param depth The depth. 650 * @return True to export MoML. 651 */ 652 @Override 653 protected boolean _isMoMLSuppressed(int depth) { 654 if (plot != null || _configureSources != null) { 655 return false; 656 } 657 return super._isMoMLSuppressed(depth); 658 } 659 660 /** Create a new plot. In this base class, it is an instance of Plot. 661 * In derived classes, it can be classes derived from Plot. 662 * @return A new plot object. 663 */ 664 protected PlotBoxInterface _newPlot() { 665 return _getImplementation().newPlot(); 666 } 667 668 /** Propagate the value of this object to the 669 * specified object. The specified object is required 670 * to be an instance of the same class as this one, or 671 * a ClassCastException will be thrown. 672 * @param destination Object to which to propagate the 673 * value. 674 * @exception IllegalActionException If the value cannot 675 * be propagated. 676 */ 677 @Override 678 protected void _propagateValue(NamedObj destination) 679 throws IllegalActionException { 680 try { 681 ((Configurable) destination).configure(_base, _source, _text); 682 } catch (Exception ex) { 683 throw new IllegalActionException(this, ex, "Propagation failed."); 684 } 685 } 686 687 /////////////////////////////////////////////////////////////////// 688 //// protected members //// 689 690 /** The base specified in configure(). */ 691 protected URL _base; 692 693 /** The source specified in configure(). */ 694 protected String _source; 695 696 /** The text specified in configure(). */ 697 protected String _text; 698 699 /////////////////////////////////////////////////////////////////// 700 //// private methods //// 701 702 /** Remove the plot from the current container, if there is one. 703 */ 704 private void _remove() { 705 _getImplementation().remove(); 706 } 707 708 /////////////////////////////////////////////////////////////////// 709 //// private members //// 710 // The bases and input streams given to the configure() method. 711 private List<URL> _configureBases = null; 712 713 private List<String> _configureSources = null; 714 715 private List<String> _configureTexts = null; 716 717 private String _configureSource; 718 719 /** Implementation of the PlotterBaseInterface. This code is used as part 720 * of the dependency injection needed for the HandSimDroid project, see 721 * $PTII/ptserver. 722 */ 723 private PlotterBaseInterface _implementation; 724}