001/* A parser for PlotML (Plot Markup Language) supporting Plot 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 032// Ptolemy imports. 033import ptolemy.plot.PlotInterface; 034 035/////////////////////////////////////////////////////////////////// 036//// PlotMLParser 037 038/** 039 This class constructs a plot from specifications 040 in PlotML (Plot Markup Language), which is an XML language. 041 This class supports extends the base class to 042 support the subset that applies to the Plot class. 043 It ignores unrecognized elements in the DTD. 044 The class contains an instance of the Microstar Ælfred XML 045 parser and implements callback methods to interpret the parsed XML. 046 The way to use this class is to construct it with a reference to 047 a Plot object and then call its parse() method. 048 049 @author Edward A. Lee 050 @version $Id$ 051 @since Ptolemy II 0.4 052 @Pt.ProposedRating Yellow (eal) 053 @Pt.AcceptedRating Red (cxh) 054 */ 055public class PlotMLParser extends PlotBoxMLParser { 056 /** Construct an parser to parse commands for the specified plot object. 057 * @param plot The plot object to which to apply the commands. 058 */ 059 public PlotMLParser(PlotInterface plot) { 060 super(plot); 061 } 062 063 /** Protected constructor allows derived classes to set _plot 064 * differently. 065 */ 066 protected PlotMLParser() { 067 } 068 069 /////////////////////////////////////////////////////////////////// 070 //// public methods //// 071 072 /** End an element. This method 073 * calls the appropriate Plot methods. 074 * Ælfred will call this method at the end of each element 075 * (including EMPTY elements). 076 * @param elementName The element type name. 077 * @exception Exception If thrown by the superclass or while calling 078 * setConnected(). 079 */ 080 @Override 081 public void endElement(String elementName) throws Exception { 082 super.endElement(elementName); 083 084 if (elementName.equals("dataset")) { 085 // Reset the default, in case it was changed for this dataset. 086 ((PlotInterface) _plot).setConnected(_connected); 087 } 088 } 089 090 /** Start a document. This method is called just before the parser 091 * attempts to read the first entity (the root of the document). 092 * It is guaranteed that this will be the first method called. 093 */ 094 @Override 095 public void startDocument() { 096 super.startDocument(); 097 _currentDataset = -1; 098 _currentPointCount = 0.0; 099 } 100 101 /** Start an element. 102 * This is called at the beginning of each XML 103 * element. By the time it is called, all of the attributes 104 * for the element will already have been reported using the 105 * attribute() method. Unrecognized elements are ignored. 106 * @param elementName The element type name. 107 * @exception XmlException If the element produces an error 108 * in constructing the model. 109 */ 110 @Override 111 public void startElement(String elementName) throws XmlException { 112 try { 113 // NOTE: The elements are alphabetical below... 114 if (elementName.equals("barGraph")) { 115 String widthSpec = (String) _attributes.get("width"); 116 String offsetSpec = (String) _attributes.get("offset"); 117 118 // NOTE: If only one of these is given, then the other 119 // is ignored. 120 if (widthSpec == null || offsetSpec == null) { 121 ((PlotInterface) _plot).setBars(true); 122 } else { 123 double width = Double.valueOf(widthSpec).doubleValue(); 124 double offset = Double.valueOf(offsetSpec).doubleValue(); 125 ((PlotInterface) _plot).setBars(width, offset); 126 } 127 } else if (elementName.equals("dataset")) { 128 String name = (String) _attributes.get("name"); 129 130 if (!((PlotInterface) _plot).getReuseDatasets() || name == null 131 || _currentDataset < 0) { 132 // reuseDatasets was not present or if it was, 133 // the current dataset does not have a name 134 // or we have not yet seen a dataset. 135 _currentDataset++; 136 _currentPointCount = 0.0; 137 } else { 138 // reuseDatasets was set to true and name is not null. 139 int possibleDataset = ((PlotInterface) _plot) 140 .getLegendDataset(name); 141 142 if (possibleDataset != -1) { 143 _currentDataset = possibleDataset; 144 } else { 145 // Did not yet have a dataset with that name. 146 _currentDataset++; 147 _currentPointCount = 0.0; 148 } 149 } 150 151 if (name != null) { 152 ((PlotInterface) _plot).addLegend(_currentDataset, name); 153 } 154 155 String connected = (String) _attributes.get("connected"); 156 157 if (connected != null) { 158 if (connected.equals("no")) { 159 ((PlotInterface) _plot).setConnected(false, 160 _currentDataset); 161 } else { 162 ((PlotInterface) _plot).setConnected(true, 163 _currentDataset); 164 } 165 } 166 167 String lineStyle = (String) _attributes.get("lineStyle"); 168 if (lineStyle != null) { 169 ((PlotInterface) _plot).setLineStyle(lineStyle, 170 _currentDataset); 171 } 172 173 String marks = (String) _attributes.get("marks"); 174 175 if (marks != null) { 176 ((PlotInterface) _plot).setMarksStyle(marks, 177 _currentDataset); 178 } 179 180 String stems = (String) _attributes.get("stems"); 181 182 if (stems != null) { 183 if (stems.equals("yes")) { 184 ((PlotInterface) _plot).setImpulses(true, 185 _currentDataset); 186 } else { 187 ((PlotInterface) _plot).setImpulses(false, 188 _currentDataset); 189 } 190 } 191 } else if (elementName.equals("default")) { 192 String connected = (String) _attributes.get("connected"); 193 194 if (connected.equals("yes")) { 195 ((PlotInterface) _plot).setConnected(true); 196 _connected = true; 197 } else { 198 ((PlotInterface) _plot).setConnected(false); 199 _connected = false; 200 } 201 202 String lineStyles = (String) _attributes.get("lineStyles"); 203 204 if (lineStyles != null) { 205 if (lineStyles.equals("yes")) { 206 ((PlotInterface) _plot).setLineStyles(true); 207 } else { 208 ((PlotInterface) _plot).setLineStyles(false); 209 } 210 } 211 212 String marks = (String) _attributes.get("marks"); 213 214 if (marks != null) { 215 ((PlotInterface) _plot).setMarksStyle(marks); 216 } 217 218 String stems = (String) _attributes.get("stems"); 219 220 if (stems.equals("no")) { 221 ((PlotInterface) _plot).setImpulses(false); 222 } else { 223 ((PlotInterface) _plot).setImpulses(true); 224 } 225 } else if (elementName.equals("m")) { 226 _addPoint(false, elementName); 227 } else if (elementName.equals("move")) { 228 _addPoint(false, elementName); 229 } else if (elementName.equals("p")) { 230 _addPoint(true, elementName); 231 } else if (elementName.equals("point")) { 232 _addPoint(true, elementName); 233 } else if (elementName.equals("reuseDatasets")) { 234 ((PlotInterface) _plot).setReuseDatasets(true); 235 } else { 236 super.startElement(elementName); 237 } 238 } catch (Exception ex) { 239 if (ex instanceof XmlException) { 240 throw (XmlException) ex; 241 } else { 242 // FIXME: Temporary for debugging. 243 System.err.println(ex.toString()); 244 ex.printStackTrace(); 245 246 String msg = "XML element \"" + elementName 247 + "\" triggers exception:\n " + ex.toString(); 248 throw new XmlException(msg, _currentExternalEntity(), 249 _parser.getLineNumber(), _parser.getColumnNumber()); 250 } 251 } 252 253 // NOTE: if super is called, this gets done twice. 254 // Any way to avoid it? 255 _attributes.clear(); 256 } 257 258 /////////////////////////////////////////////////////////////////// 259 //// protected members //// 260 261 /** The default connected state. */ 262 protected boolean _connected = true; 263 264 /** The current dataset number in a "dataset" element. */ 265 protected int _currentDataset = -1; 266 267 /** A count within the current dataset, in case no x value is given. */ 268 protected double _currentPointCount = 0.0; 269 270 /////////////////////////////////////////////////////////////////// 271 //// protected methods //// 272 273 /** Add a point based on the current attributes. 274 * If the first argument is true, connect it to the previous point. 275 * The second argument is the element name, used for error reporting. 276 * @param connected If true, connect to the previous point. 277 * @param element The name of the element. 278 * @exception Exception If there is a problem adding the point. 279 */ 280 protected void _addPoint(boolean connected, String element) 281 throws Exception { 282 String xSpec = (String) _attributes.get("x"); 283 double x; 284 285 if (xSpec == null) { 286 // No x value given. Use _currentPointCount. 287 x = _currentPointCount; 288 _currentPointCount += 1.0; 289 } else { 290 // NOTE: We use Double.parseDouble() here, which breaks 291 // Java 1.1 compatibility, but means we don't allocate a Double. 292 //x = (Double.valueOf(xSpec)).doubleValue(); 293 x = Double.parseDouble(xSpec); 294 } 295 296 String ySpec = (String) _attributes.get("y"); 297 _checkForNull(ySpec, "No y value for element \"" + element + "\""); 298 299 // NOTE: We use Double.parseDouble() here, which breaks 300 // Java 1.1 compatibility, but means we don't allocate a Double. 301 //double y = (Double.valueOf(ySpec)).doubleValue(); 302 double y = Double.parseDouble(ySpec); 303 304 String lowSpec = (String) _attributes.get("lowErrorBar"); 305 String highSpec = (String) _attributes.get("highErrorBar"); 306 307 if (lowSpec == null && highSpec == null) { 308 ((PlotInterface) _plot).addPoint(_currentDataset, x, y, connected); 309 } else { 310 double low; 311 double high; 312 313 if (lowSpec != null) { 314 low = Double.valueOf(lowSpec).doubleValue(); 315 } else { 316 low = x; 317 } 318 319 if (highSpec != null) { 320 high = Double.valueOf(highSpec).doubleValue(); 321 } else { 322 high = x; 323 } 324 325 ((PlotInterface) _plot).addPointWithErrorBars(_currentDataset, x, y, 326 null, low, high, connected); 327 } 328 } 329}