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}