001/* A tableau representing a plot window.
002
003 Copyright (c) 2000-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 */
027package ptolemy.actor.gui;
028
029import java.io.InputStream;
030import java.net.MalformedURLException;
031import java.net.URI;
032import java.net.URL;
033
034import javax.swing.JFrame;
035
036import ptolemy.kernel.attributes.URIAttribute;
037import ptolemy.kernel.util.Attribute;
038import ptolemy.kernel.util.IllegalActionException;
039import ptolemy.kernel.util.InternalErrorException;
040import ptolemy.kernel.util.NameDuplicationException;
041import ptolemy.kernel.util.NamedObj;
042import ptolemy.plot.Plot;
043import ptolemy.plot.PlotBox;
044import ptolemy.plot.plotml.PlotMLParser;
045import ptolemy.util.MessageHandler;
046
047///////////////////////////////////////////////////////////////////
048//// PlotTableau
049
050/**
051 A tableau representing a plot in a toplevel window.
052 The URL that is viewed is given by the <i>uri</i> parameter, and
053 can be either an absolute URL, a system fileName, or a resource that
054 can be loaded relative to the classpath.  For more information about how
055 the URL is specified, see MoMLApplication.specToURL().
056 <p>
057 The plot frame itself must be an instance of PlotTableauFrame,
058 and must be created by the caller.
059 As with other tableaux, this is an entity that is contained by
060 an effigy of a model.
061 There can be any number of instances of this class in an effigy.
062
063 @author  Edward A. Lee
064 @version $Id$
065 @since Ptolemy II 2.1
066 @Pt.ProposedRating Yellow (eal)
067 @Pt.AcceptedRating Red (cxh)
068 @see Effigy
069 @see PlotTableauFrame
070 @see MoMLApplication#specToURL(String)
071 */
072public class PlotTableau extends Tableau {
073    /** Construct a new tableau for the model represented by the given effigy.
074     *  Use setFrame() to specify the plot frame after construction.
075     *  @param container The container.
076     *  @param name The name.
077     *  @exception IllegalActionException If the container does not accept
078     *   this entity (this should not occur).
079     *  @exception NameDuplicationException If the name coincides with an
080     *   attribute already in the container.
081     */
082    public PlotTableau(Effigy container, String name)
083            throws IllegalActionException, NameDuplicationException {
084        super(container, name);
085
086        uri = new URIAttribute(this, "uri");
087    }
088
089    ///////////////////////////////////////////////////////////////////
090    ////                         public parameters                 ////
091
092    /** The URI to display. */
093    public URIAttribute uri;
094
095    ///////////////////////////////////////////////////////////////////
096    ////                         public methods                    ////
097
098    /** If the argument is the <i>uri</i> parameter, then open the
099     *  specified URI and display its contents.
100     *  @param attribute The attribute that changed.
101     *  @exception IllegalActionException If the URL cannot be opened,
102     *   or if the base class throws it.
103     */
104    @Override
105    public void attributeChanged(Attribute attribute)
106            throws IllegalActionException {
107        if (attribute == uri) {
108            try {
109                URL toRead = new URL(uri.getURI().toString());
110                _parseURL(toRead);
111            } catch (MalformedURLException ex) {
112                throw new IllegalActionException(this, ex,
113                        "Invalid URL specification.");
114            }
115        } else {
116            super.attributeChanged(attribute);
117        }
118    }
119
120    /** Set the top-level window associated with this tableau.
121     *  @param frame The top-level window associated with the tableau.
122     *  @exception IllegalActionException If the frame is not an instance
123     *   of PlotTableauFrame.
124     */
125    @Override
126    public void setFrame(JFrame frame) throws IllegalActionException {
127        if (frame != null && !(frame instanceof PlotTableauFrame)) {
128            throw new IllegalActionException(this,
129                    "Frame for PlotTableau must be an instance of "
130                            + "PlotTableauFrame.");
131        }
132
133        super.setFrame(frame);
134        if (frame != null) {
135            ((PlotTableauFrame) frame).setTableau(this);
136        }
137    }
138
139    /** Make this tableau visible by calling setVisible(true), and
140     *  raising or deiconifying its window.
141     *  If no frame has been set, then create one, an instance of
142     *  PlotTableauFrame.  If a URL has been specified but not yet
143     *  processed, then process it.
144     */
145    @Override
146    public void show() {
147        JFrame frame = getFrame();
148
149        if (frame == null) {
150            PlotTableauFrame newFrame = new PlotTableauFrame(this);
151            newFrame.plot.setButtons(true);
152
153            try {
154                setFrame(newFrame);
155            } catch (IllegalActionException ex) {
156                throw new InternalErrorException(ex);
157            }
158        }
159
160        if (_toRead != null) {
161            _parseURL(_toRead);
162        }
163
164        super.show();
165    }
166
167    ///////////////////////////////////////////////////////////////////
168    ////                         private methods                   ////
169
170    /** Read from the specified URL in PlotML format.
171     *  If there is no plot frame yet, then defer.
172     *  Report any errors.
173     *  @param url The URL to read from.
174     */
175    private void _parseURL(URL url) {
176        try {
177            PlotTableauFrame frame = (PlotTableauFrame) getFrame();
178
179            if (frame != null) {
180                // FIXME: Should use a HistogramMLParser to get a histogram
181                // view... But how can we know that is what is wanted?
182                PlotMLParser parser = new PlotMLParser((Plot) frame.plot);
183                InputStream stream = url.openStream();
184                parser.parse(url, stream);
185                stream.close();
186                _toRead = null;
187            } else {
188                // There is no plotter yet.  Have to defer.
189                _toRead = url;
190            }
191        } catch (Exception ex) {
192            MessageHandler.error(
193                    "Failed to read plot data: " + url.toExternalForm(), ex);
194        }
195    }
196
197    ///////////////////////////////////////////////////////////////////
198    ////                         private members                   ////
199    // URL of deferred read.
200    private URL _toRead = null;
201
202    ///////////////////////////////////////////////////////////////////
203    ////                         inner classes                     ////
204
205    /** A factory that creates a plot tableau for Ptolemy models.
206     */
207    public static class Factory extends TableauFactory {
208        /** Create a factory with the given name and container.
209         *  @param container The container.
210         *  @param name The name.
211         *  @exception IllegalActionException If the container is incompatible
212         *   with this attribute.
213         *  @exception NameDuplicationException If the name coincides with
214         *   an attribute already in the container.
215         */
216        public Factory(NamedObj container, String name)
217                throws IllegalActionException, NameDuplicationException {
218            super(container, name);
219        }
220
221        ///////////////////////////////////////////////////////////////////
222        ////                         public methods                    ////
223
224        /** If the specified effigy already contains a tableau named
225         *  "plotTableau", then return that tableau; otherwise, create
226         *  a new instance of PlotTableau in the specified
227         *  effigy, and name it "plotTableau".  If the specified
228         *  effigy is not an instance of PlotEffigy, then do not
229         *  create a tableau and return null.  It is the
230         *  responsibility of callers of this method to check the
231         *  return value and call show().
232         *
233         *  @param effigy The effigy.
234         *  @return A plot tableau, or null if one cannot be
235         *    found or created.
236         *  @exception Exception If the factory should be able to create a
237         *   tableau for the effigy, but something goes wrong.
238         */
239        @Override
240        public Tableau createTableau(Effigy effigy) throws Exception {
241            if (effigy instanceof PlotEffigy) {
242                // Indicate to the effigy that this factory contains effigies
243                // offering multiple views of the effigy data.
244                effigy.setTableauFactory(this);
245
246                // First see whether the effigy already contains an
247                // PlotTableau.
248                PlotTableau tableau = (PlotTableau) effigy
249                        .getEntity("plotTableau");
250
251                if (tableau == null) {
252                    tableau = new PlotTableau(effigy, "plotTableau");
253                }
254
255                PlotBox plotBox = (PlotBox) ((PlotEffigy) effigy).getPlot();
256
257                if (plotBox != null) {
258                    // Hook into the existing plot.
259                    PlotTableauFrame plotterFrame = new PlotTableauFrame(
260                            tableau, plotBox);
261                    tableau.setFrame(plotterFrame);
262                }
263
264                URI uri = effigy.uri.getURI();
265
266                if (uri != null) {
267                    tableau.uri.setURI(uri);
268                }
269
270                // Don't call show() here.  If show() is called here,
271                // then you can't set the size of the window after
272                // createTableau() returns.  This will affect how
273                // centering works.
274                return tableau;
275            } else {
276                return null;
277            }
278        }
279    }
280}