001/* A parser for PlotML (Plot Markup Language) supporting Histogram commands.
002
003 Copyright (c) 1998-2016 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.plot.plotml;
029
030import com.microstar.xml.XmlException;
031
032import ptolemy.plot.Histogram;
033
034///////////////////////////////////////////////////////////////////
035//// HistogramMLParser
036
037/**
038 This class constructs a histogram from specifications
039 in PlotML (Plot Markup Language), which is an XML language.
040 This class supports extends the base class to
041 ensure that the associated plotter is an instance of Histogram.
042 It ignores unrecognized elements in the DTD.
043 The class contains an instance of the Microstar Ælfred XML
044 parser and implements callback methods to interpret the parsed XML.
045 The way to use this class is to construct it with a reference to
046 a Plot object and then call its parse() method.
047
048 @author Edward A. Lee
049 @version $Id$
050 @since Ptolemy II 0.4
051 @Pt.ProposedRating Red (eal)
052 @Pt.AcceptedRating Red (cxh)
053 */
054public class HistogramMLParser extends PlotMLParser {
055    /** Construct an parser to parse commands for the specified plot object.
056     *  @param plot The plot object to which to apply the commands.
057     */
058    public HistogramMLParser(Histogram plot) {
059        _plot = plot;
060    }
061
062    ///////////////////////////////////////////////////////////////////
063    ////                         public methods                    ////
064
065    /** End an element. Override the base class to ignore non-histogram
066     *  operations.
067     *  Ælfred will call this method at the end of each element
068     *  (including EMPTY elements).
069     *  @param elementName The element type name.
070     *  @exception Exception If thrown by the base class.
071     */
072    @Override
073    public void endElement(String elementName) throws Exception {
074        if (elementName.equals("bars") || elementName.equals("dataset")) {
075            // Ignore
076        } else {
077            super.endElement(elementName);
078        }
079    }
080
081    /** Start an element.
082     *  This is called at the beginning of each XML
083     *  element.  By the time it is called, all of the attributes
084     *  for the element will already have been reported using the
085     *  attribute() method.  Unrecognized elements are ignored.
086     *  @param elementName The element type name.
087     *  @exception XmlException If the element produces an error
088     *   in constructing the model.
089     */
090    @Override
091    public void startElement(String elementName) throws XmlException {
092        try {
093            if (elementName.equals("barGraph")) {
094                // Override the base class to ignore things irrelevant
095                // to histograms...
096                String widthSpec = (String) _attributes.get("width");
097                String offsetSpec = (String) _attributes.get("offset");
098
099                // NOTE: If only one of these is given, then the other
100                // is ignored.
101                if (widthSpec != null && offsetSpec != null) {
102                    double width = Double.valueOf(widthSpec).doubleValue();
103                    double offset = Double.valueOf(offsetSpec).doubleValue();
104                    ((Histogram) _plot).setBars(width, offset);
105                }
106            } else if (elementName.equals("bin")) {
107                // Ignore if plot is not an instance of Histogram.
108                if (_plot instanceof Histogram) {
109                    Histogram histogram = (Histogram) _plot;
110                    String widthSpec = (String) _attributes.get("width");
111                    String offsetSpec = (String) _attributes.get("offset");
112
113                    // NOTE: If only one of these is given, then the other
114                    // is ignored.
115                    if (widthSpec != null && offsetSpec != null) {
116                        double width = Double.valueOf(widthSpec).doubleValue();
117                        histogram.setBinWidth(width);
118
119                        double offset = Double.valueOf(offsetSpec)
120                                .doubleValue();
121                        histogram.setBinOffset(offset);
122                    }
123                }
124            } else if (elementName.equals("dataset")) {
125                // Override the base class to ignore things irrelevant
126                // to histograms...
127                _currentDataset++;
128                _currentPointCount = 0.0;
129
130                String name = (String) _attributes.get("name");
131
132                if (name != null) {
133                    _plot.addLegend(_currentDataset, name);
134                }
135            } else if (elementName.equals("default")) {
136                // Override the base class to ignore things irrelevant
137                // to histograms...
138            } else {
139                super.startElement(elementName);
140            }
141        } catch (Exception ex) {
142            if (ex instanceof XmlException) {
143                throw (XmlException) ex;
144            } else {
145                String msg = "XML element \"" + elementName
146                        + "\" triggers exception:\n  " + ex.toString();
147                throw new XmlException(msg, _currentExternalEntity(),
148                        _parser.getLineNumber(), _parser.getColumnNumber());
149            }
150        }
151
152        // NOTE: if super is called, this gets done three times...
153        // Any way to avoid it?
154        _attributes.clear();
155    }
156
157    ///////////////////////////////////////////////////////////////////
158    ////                         protected methods                 ////
159
160    /** Add a point based on the current attributes.
161     *  The second argument is the element name, used for error reporting.
162     *  @param connected Ignored.
163     *  @param element The name of the element.
164     *  @exception Exception if thrown whil parsing the y attribute.
165     */
166    @Override
167    protected void _addPoint(boolean connected, String element)
168            throws Exception {
169        String ySpec = (String) _attributes.get("y");
170        _checkForNull(ySpec, "No y value for element \"" + element + "\"");
171
172        // NOTE: Do not use parseDouble() to maintain Java 1.1 compatibility.
173        double y = Double.valueOf(ySpec).doubleValue();
174        ((Histogram) _plot).addPoint(_currentDataset, y);
175    }
176}