001/* parser for pxgraph command line arguments and binary files. 002 003 @Author: Edward A. Lee and Christopher Hylands 004 005 @Version: $Id$ 006 007 @Copyright (c) 1997-2014 The Regents of the University of California. 008 All rights reserved. 009 010 Permission is hereby granted, without written agreement and without 011 license or royalty fees, to use, copy, modify, and distribute this 012 software and its documentation for any purpose, provided that the 013 above copyright notice and the following two paragraphs appear in all 014 copies of this software. 015 016 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 017 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 018 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 019 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 020 SUCH DAMAGE. 021 022 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 023 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 024 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 025 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 026 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 027 ENHANCEMENTS, OR MODIFICATIONS. 028 029 PT_COPYRIGHT_VERSION_2 030 COPYRIGHTENDKEY 031 */ 032package ptolemy.plot.compat; 033 034import java.io.BufferedInputStream; 035import java.io.DataInputStream; 036import java.io.EOFException; 037import java.io.FileInputStream; 038import java.io.FileNotFoundException; 039import java.io.IOException; 040import java.io.InputStream; 041import java.io.StreamTokenizer; 042import java.io.StringReader; 043import java.net.MalformedURLException; 044import java.net.URL; 045import java.util.Vector; 046 047import ptolemy.plot.CmdLineArgException; 048import ptolemy.plot.Plot; 049import ptolemy.plot.PlotBox; 050 051/////////////////////////////////////////////////////////////////// 052//// PxgraphParser 053 054/** 055This class provides backwards compatibility with an older plotting 056program, pxgraph. It provides two methods, one for parsing command-line 057arguments, and one for reading binary data from a file. In pxgraph, 058the binary files have no format information; all format information 059is provided by command line arguments. 060 061<p>Below we describe the <code>pxgraph</code> arguments. The text is 062 based on the <code>xgraph</code> Unix man page written by David 063 Harrison (University of California). To see the command line 064 options, you can type 065 <code>pxgraph -help</code>.</p> 066 067<p>The <code>pxgraph</code> program draws a graph on a display given 068 data read from either data files or from standard input if one of 069 the arguments is a dash. It can display up to 64 independent data 070 sets using different colors and/or line styles for each set. It 071 annotates the graph with a title, axis labels, grid lines or tick 072 marks, grid labels, and a legend. There are options to control the 073 appearance of most components of the graph.</p> 074 075<p>The input format is similar to <code>graph(<i>1G</i>)</code> but differs 076 slightly. The data consists of a number of <I>data</I> <I>sets</I>. Data 077 sets are separated by a blank line. A new data set is also 078 assumed at the start of each input file. A data set consists 079 of an ordered list of points of the form <code><i>directive</i> 080 X Y</code>.</p> 081 082<p>The directive is either <code>draw</code> or <code>move</code> and can 083 be omitted (Note that with binary data files, you must have a directive, 084 the above statement only applies to ascii format data files). If the 085 directive is <code>draw</code>, a line will be drawn 086 between the previous point and the current point (if a line 087 graph is chosen). Specifying a <code>move</code> directive tells 088 xgraph not to draw a line between the points. If the directive 089 is omitted, <code>draw</code> is assumed for all points in a data 090 set except the first point where <code>move</code> is assumed. The 091 <code>move</code> directive is used most often to allow discontinuous 092 data in a data set.</p> 093 094<p>After <code>pxgraph</code> has read the data, it will create a new window 095 to graphically display the data.</p> 096 097<p> Once the window has been opened, all of the data sets will 098 be displayed graphically (subject to the options explained 099 below) with a legend in the upper right corner of the 100 screen. To zoom in on a portion of the graph, depress a 101 mouse button in the window and sweep out a region. <code>pxgraph</code> 102 will then the window will be redrawn with just that portion of 103 the graph. <code>pxgraph</code> also presents four control buttons in 104 the lower left corner of each window: <code>Exit</code>, 105 <code>Print</code>, <code>HTML</code> and <code>About</code>.</p> 106 107<p>The <code>Exit</code> button will exit the process. You can also 108 type <code>Control-D</code>, <code>Control-C</code> or <code>q</code> 109 to exit.</p> 110 111<p>The <code>Print</code> button brings up a print dialog window.</p> 112 113<p>The <code>About</code> button brings up a message about 114<code>pxgraph</code>.</p> 115 116<p>The <code>HTML</code> button prints an HTML file to stdout that 117 can be used to display the file with applet <code>Plot</code> classes 118 (Experimental).</p> 119 120<p><code>pxgraph</code> accepts a large number of command line options. 121 A list of these options is given below.</p> 122 123<dl> 124 <dt><code>=<i>W</i>x<i>H</i>+<i>X</i>+<i>Y</i></code></dt> 125 <dd>Specifies the initial size and location of the pxgraph 126 window.</dd> 127 128 <dt> <code>-<i><digit> <name></i></code></dt> 129 <dd> These options specify the data 130 set name for the corresponding data set. The digit 131 should be in the range 0 to 63. This name will be 132 used in the legend.</dd> 133 134 <dt><code>-bar</code></dt> 135 <dd>Specifies that vertical bars should be drawn from the 136 data points to a base point which can be specified with 137 <code>-brb</code>. 138 Usually, the <code>-nl</code> flag is used with this option. 139 The point itself is located at the center of the bar.</dd> 140 141 <dt><code>-bb</code></dt> 142 <dd>Draw a bounding box around the data region. This is 143 very useful if you prefer to see tick marks rather than 144 grid lines (see <code>-tk</code>). 145 <b>Ignored in the Java version because the plotting area is a different 146 color than the border where the axes are labeled.</b></dd> 147 148 <dt><code>-bd</code> <code><i><color></i></code></dt> 149 <dd>This specifies the border color of the <code>pxgraph</code> window. 150 <b>Unsupported in the Java version.</b></dd> 151 152 <dt><code>-bg</code> <code><i><color></i></code></dt> 153 <dd>Background color of the area where the labels and legend are rendered. 154 <b>In the Java version, this argument takes hexadecimal color values 155 (<code>fffff</code>), not color names.</b> Note that the background 156 of the data plotting region is always white because the dataset colors 157 were designed for a white background.</dd> 158 159 <dt><a name="bigendianFlag"><code>-bigendian</code></a></dt> 160 <dd>Data files are in big-endian, or network binary format. 161 See the <code>-binary</code> command line argument documentation 162 below for details about the format. 163 If you are on a little-endian machine, such as a machine 164 with an Intel x86 chip, and you would like to read a binary 165 format file, created on a big-endian machine, such as a Sun SPARC, 166 use the <code>-bigendian</code> flag.</dd> 167 168 <dt><a name="binaryFlag"><code>-binary</code></a></dt> 169 <dd>Data files are in a binary format. 170 The endian-ism of the data depends on which of the two 171 subformats below are chosen. 172 The <code>-binary</code> 173 argument is the primary difference between <code>xgraph</code> 174 and <code>pxgraph</code>. The 175 <A HREF="http://ptolemy.eecs.berkeley.edu">Ptolemy Project</A> software 176 makes extensive use of <code>-binary</code>. 177 <br>There are two binary formats, both of which use 4 byte floats. 178 <ol> 179 <li>If the first byte of the data file is not a <code>d</code>, then 180 we assume that the file contains 4 byte floats in big-endian ordering 181 with no plot commands. 182 <li>If the first byte of the data file is a <code>d</code>, then 183 we assume that the plot commands are encoded as single characters, 184 and the numeric data is a 4 byte float encoded in the 185 native endian format of the machine that the java interpreter is 186 running on. 187 <br>The commands are encoded as follows: 188 <dl> 189 <dt> <code>d <I><4byte float> <4byte float></I></code></dt> 190 <dd> Draw a X, Y point</dd> 191 <dt> <code>e</code></dt> 192 <dd> End of dataset</dd> 193 <dt> <code>n <I><dataset name></I>\</code></dt> 194 <dd> New dataset name, ends in <code>\</code></dd> 195 <dt> <code>m <I><4byte float> <4byte float></I></code></dt> 196 <dd> Move to a X, Y point.</dd> 197 </dl> 198 </li> 199 </ol> 200 <br>To view a binary plot file under unix, we can use the 201 <code>od</code> command. Note that the first character is a <code>d</code> 202 followed by eight bytes of data consisting of two floats of four bytes. 203 <pre> 204 cxh@carson 324% od -c data/integrator1.plt 205 0000000 d \0 \0 \0 \0 \0 \0 \0 \0 d ? 200 \0 \0 ? 200 206 0000020 \0 \0 d @ \0 \0 \0 @ , 314 315 d @ @ \0 \0 207 </pre> 208 <br>For further information about endian-ism, see the 209 <code>-bigendian</code> and <code>-littleendian</code> command 210 line argument documentation. 211 </dd> 212 213 <dt><code>-brb</code> <code><i><base></i></code></dt> 214 <dd>This specifies the base for a bar graph. By default, 215 the base is zero. 216 <b>Unsupported in the Java version.</b></dd> 217 218 <dt><code>-brw</code> <code><i><width></i></code></dt> 219 <dd>This specifies the width of bars in a bar graph. The 220 amount is specified in the user units. By default, 221 a bar one pixel wide is drawn.</dd> 222 223 <dt><code>-bw</code> <code><i><size></i></code></dt> 224 <dd>Border width (in pixels) of the <code>pxgraph</code> window. 225 <b>Unsupported in the Java version.</b></dd> 226 227 <dt><code>-db</code></dt> 228 <dd>Causes xgraph to run in synchronous mode and prints out 229 the values of all known defaults.</dd> 230 231 <dt><code>-fg</code> <code><i><color></i></code></dt> 232 <dd>Foreground color. This color is used to draw all text 233 and the normal grid lines in the window. 234 <b>In the Java version, this argument takes hexadecimal color values 235 (<code>fffff</code>), not color names.</b></dd> 236 237 <dt><code>-gw</code></dt> 238 <dd> Width, in pixels, of normal grid lines. 239 <b>Unsupported in the Java version.</b></dd> 240 241 <dt><code>-gs</code></dt> 242 <dd> Line style pattern of normal grid lines.</dd> 243 244 <dt><code>-impulses</code></dt> 245 <dd>Draw a line from any plotted point down to the x axis. 246 (This argument is not present in the X11 <code>pxgraph</code>, 247 but it is similar to <code>-nl -bar</code>).</dd> 248 249 <dt><code>-lf</code> <code><i><fontname></i></code></dt> 250 <dd>Label font. All axis labels and grid labels are drawn 251 using this font. 252 <b>Note that the Java version does not use X11 style font specification.</b> 253 In the Java version, fonts may be specified as 254 <menu> 255 <li><code><i>fontname</i></code>, where 256 <code><i>fontname</i></code> is one of <code>helvetica</code>, 257 <code>TimesRoman</code>, <code>Courier</code>, <code>Dialog</code>, 258 <code>DialogInput</code>, <code>ZapfDingbats</code>. 259 260 <li><code><i>fontname</i>-<i>style</i></code>, where 261 <code><i>style</i></code> is one of 262 <code>PLAIN</code>, <code>ITALIC</code>, <code>BOLD</code>, 263 i.e. <code>helvetica-ITALIC</code> 264 <li><code><i>fontname</i>-<i>size</i></code>, or 265 <li><code><i>fontname</i>-<i>style</i>-<i>size</i></code>, where 266 <code><i>size</i></code> is an integer font size in points. 267 </menu> 268 The default is <code>helvetica-PLAIN-12</code>. 269 </dd> 270 271 <dt><a name="littleendian"><code>-littleendian</code></a></dt> 272 <dd>Data files are in little-endian, or x86 binary format. 273 See the <code>-binary</code> command line argument documentation 274 above for details about the format. 275 If you are on a big-endian machine, such as a Sun Sparc, 276 and you would like to read a binary 277 format file created on a little-endian machine, such as Intel x86 278 machine, then use the <code>-littleendian</code> flag.</dd> 279 280 <dt><code>-lnx</code></dt> 281 <dd>Specifies a logarithmic X axis. Grid labels represent 282 powers of ten. If <code>-lnx</code> is present, then 283 x values must be greater than zero.</dd> 284 285 <dt><code>-lny</code></dt> 286 <dd>Specifies a logarithmic Y axis. Grid labels represent 287 powers of ten. If <code>-lny</code> is present, then 288 y values must be greater than zero.</dd> 289 290 <dt><code>-lw</code> <code><i>width</i></code></dt> 291 <dd>Specifies the width of the data lines in pixels. The 292 default is zero. 293 <b>Unsupported in the Java version.</b></dd> 294 295 <dt><code>-lx</code> <code><i><xl,xh></i></code></dt> 296 <dd>This option limits the range of the X axis to the 297 specified interval. This (along with <code>-ly</code>) can be used 298 to zoom in on a particularly interesting portion of a 299 larger graph.</dd> 300 301 <dt><code>-ly</code> <code><i><yl,yh></i></code></dt> 302 <dd>This option limits the range of the Y axis to the 303 specified interval.</dd> 304 305 <dt><code>-m</code></dt> 306 <dd>Mark each data point with a distinctive marker. There 307 are eight distinctive markers used by xgraph. These 308 markers are assigned uniquely to each different line 309 style on black and white machines and varies with each 310 color on color machines.</dd> 311 312 <dt><code>-M</code></dt> 313 <dd>Similar to <code>-m</code> but markers are assigned uniquely to each 314 eight consecutive data sets (this corresponds to each 315 different line style on color machines).</dd> 316 317 <dt><code>-nl</code></dt> 318 <dd>Turn off drawing lines. When used with <code>-m</code>, 319 <code>-M</code>, <code>-p</code>, or <code>-P</code> this can be used 320 to produce scatter plots. When used with -bar, it can be used to 321 produce standard bar graphs.</dd> 322 323 <dt><code>-o</code> <code><i>output filename</i></code></dt> 324 <dd>The name of the file to place the print output in. Currently 325 defaults to <code>/tmp/t.ps</code>. See also the 326 <code>-print</code> option.</dd> 327 328 <dt><code>-p</code></dt> 329 <dd>Marks each data point with a small marker (pixel 330 sized). This is usually used with the -nl option for 331 scatter plots.</dd> 332 333 <dt><code>-P</code></dt> 334 <dd>Similar to <code>-p</code> but marks each pixel with a large dot.</dd> 335 336 <dt><code>-print</code></dt> 337 <dd>Bring up the print dialog immediately upon startup. Unfortunately, 338 there is no way to automatically print in JDK1.1, the user must hit 339 the <code>Ok</code> button. See also the <code>-o</code> option.</dd> 340 341 <dt><code>-rv</code></dt> 342 <dd>Reverse video. On black and white displays, this will 343 invert the foreground and background colors. The 344 behaviour on color displays is undefined.</dd> 345 346 <dt><code>-t</code> <code><i><string></i></code></dt> 347 <dd>Title of the plot. This string is centered at the top 348 of the graph.</dd> 349 350 <dt><code>-tf</code> <code><i><fontname></i></code></dt> 351 <dd>Title font. This is the name of the font to use for 352 the graph title. See the <code>-lf</code> description above 353 for how to specify fonts. 354 The default is <code>helvetica-BOLD-14</code></dd> 355 356 <dt><code>-tk</code></dt> 357 <dd>This option causes <code>pxgraph</code> to draw tick marks rather 358 than full grid lines. The <code>-bb</code> option is also useful 359 when viewing graphs with tick marks only.</dd> 360 361 <dt><code>-x</code> <code><i><unitname></i></code></dt> 362 <dd>This is the unit name for the X axis. Its default is "X".</dd> 363 364 <dt><code>-y</code> <code><i><unitname></i></code></dt> 365 <dd>This is the unit name for the Y axis. Its default is "Y".</dd> 366 367 <dt><code>-zg</code> <code><i><color></i></code></dt> 368 <dd>This is the color used to draw the zero grid line. 369 <b>Unsupported in the Java version.</b></dd> 370 371 <dt><code>-zw</code> <code><i><width></i></code></dt> 372 <dd>This is the width of the zero grid line in pixels. 373 <b>Unsupported in the Java version.</b></dd> 374</dl> 375 376<b><a name="pxgraphScriptCompatibilityIssues">Compatibility Issues</a></b> 377<p>Various compatibility issues are documented above in <b>bold</b>. 378 Below are some other issues:</p> 379<ol> 380 <li>The original <code>xgraph</code> program allowed many formatting 381 directives inside the file. This version only supports 382 <code>draw</code> and <code>move</code>.</li> 383 <li>To read from standard input, specify a dash on the command line. 384 Note that mixing reading from standard in and from files is not 385 well supported.</li> 386 <li>This original <code>xgraph</code> program allowed blank lines 387 to separate datasets. This version does not. Instead, use the 388 <code>move <i>X</i> <i>Y</i></code> directive.</li> 389 <li>This version does not support X resources. 390 <li>The Java version of <code>pxgraph</code> takes longer to start up 391 than the X11 version. This is an inherent problem with standalone 392 Java applications. One guess is that most of the startup time comes 393 from paging in the shared libraries.</li> 394</ol> 395 396<p> 397 For further information about this tool, see the 398 <a href="http://ptolemy.eecs.berkeley.edu/java/ptplot">Java Plot Website</a>.</p> 399 400@author Edward A. Lee and Christopher Hylands 401 402 @version $Id$ 403 @since Ptolemy II 0.4 404 @Pt.ProposedRating red (eal) 405 @Pt.AcceptedRating red (cxh) 406 @see PxgraphApplication 407 @see PxgraphApplet 408 */ 409public class PxgraphParser { 410 /** Construct a parser to configure the specified plot. 411 * @param plot The Plot object that is configured. 412 */ 413 public PxgraphParser(Plot plot) { 414 _plot = plot; 415 } 416 417 /////////////////////////////////////////////////////////////////// 418 //// public methods //// 419 420 /** Parse pxgraph style command-line arguments. 421 * @param args A set of command-line arguments. 422 * @return The number of arguments read. 423 * @exception CmdLineArgException If there is a problem parsing 424 * the command line arguments. 425 * @exception FileNotFoundException If a file is specified that is not 426 * found. 427 * @exception IOException If an error occurs reading an input file. 428 */ 429 public int parseArgs(String[] args) 430 throws CmdLineArgException, FileNotFoundException, IOException { 431 return parseArgs(args, null); 432 } 433 434 /** Parse pxgraph style command-line arguments, using the specified 435 * base URL for any relative URL references. 436 * @param args A set of command-line arguments. 437 * @param base A base URL for relative URL references, or null if 438 * there is none. 439 * @return The number of arguments read. 440 * @exception CmdLineArgException If there is a problem parsing 441 * the command line arguments. 442 * @exception FileNotFoundException If a file is specified that is not 443 * found. 444 * @exception IOException If an error occurs reading an input file. 445 */ 446 public int parseArgs(String[] args, URL base) 447 throws CmdLineArgException, FileNotFoundException, IOException { 448 int i = 0; 449 int j; 450 int argumentsRead = 0; 451 452 // If we see both -nl and -bar, assume we do a stem plot. 453 boolean sawbararg = false; // Saw -bar arg. 454 boolean sawnlarg = false; // Saw -nl arg. 455 String savedmarks = "none"; // Save _marks in case we have -P -bar -nl. 456 _binary = false; // Read a binary xgraph file. 457 458 int width = 400; 459 int height = 400; 460 461 String arg; 462 String[] unsupportedOptions = { "-bd", "-brb", "-bw", "-gw", "-lw", 463 "-zg", "-zw" }; 464 465 // True if we saw an - arg, which means read from stdin 466 boolean sawDash = false; 467 468 while (args != null && i < args.length 469 && (args[i].startsWith("-") || args[i].startsWith("="))) { 470 arg = args[i++]; 471 472 if (arg.startsWith("-")) { 473 // Search for unsupported options that take arguments 474 boolean badarg = false; 475 476 for (j = 0; j < unsupportedOptions.length; j++) { 477 if (arg.equals(unsupportedOptions[j])) { 478 System.err.println("Warning: pxgraph: " + arg 479 + " is not supported"); 480 i++; 481 badarg = true; 482 } 483 } 484 485 if (badarg) { 486 continue; 487 } 488 489 if (arg.equals("-bb")) { 490 // We ignore -bb because the Java version of pxgraph plot 491 // region is a different color from the surrounding region. 492 continue; 493 } else if (arg.equals("-bg")) { 494 _plot.setBackground(PlotBox.getColorByName(args[i++])); 495 continue; 496 } else if (arg.equals("-brw")) { 497 // -brw <width> BarWidth Bars: 498 // We default the baroffset to 0 here if the value does 499 // not include a comma. 500 double[] spec = _parseDoubles(args[i++]); 501 502 if (spec.length == 1) { 503 _plot.setBars(spec[0], 0); 504 } else { 505 _plot.setBars(spec[0], spec[1]); 506 } 507 508 continue; 509 } else if (arg.equals("-lf")) { 510 // -lf <labelfont> 511 _plot.setLabelFont(args[i++]); 512 continue; 513 } else if (arg.equals("-lx")) { 514 double[] spec = _parseDoubles(args[i++]); 515 516 if (spec.length == 1) { 517 throw new CmdLineArgException( 518 "Failed to parse `" + arg + "'"); 519 } else { 520 _plot.setXRange(spec[0], spec[1]); 521 } 522 523 continue; 524 } else if (arg.equals("-ly")) { 525 double[] spec = _parseDoubles(args[i++]); 526 527 if (spec.length == 1) { 528 throw new CmdLineArgException( 529 "Failed to parse `" + arg + "'"); 530 } else { 531 _plot.setYRange(spec[0], spec[1]); 532 } 533 534 continue; 535 } else if (arg.equals("-t")) { 536 // -t <title> TitleText "An X Graph" 537 String title = args[i++]; 538 _plot.setTitle(title); 539 continue; 540 } else if (arg.equals("-tf")) { 541 // -tf <titlefont> 542 _plot.setTitleFont(args[i++]); 543 continue; 544 } else if (arg.equals("-x")) { 545 // -x <unitName> XUnitText XLabel: 546 _plot.setXLabel(args[i++]); 547 continue; 548 } else if (arg.equals("-y")) { 549 // -y <unitName> YUnitText YLabel: 550 _plot.setYLabel(args[i++]); 551 continue; 552 } else if (arg.equals("-bar")) { 553 //-bar BarGraph Bars: on Marks: none Lines: off 554 // If we saw the -nl arg, then assume impulses 555 sawbararg = true; 556 557 if (sawnlarg) { 558 _plot.setImpulses(true); 559 } else { 560 _plot.setBars(true); 561 _plot.setMarksStyle("none"); 562 } 563 564 _plot.setConnected(false); 565 continue; 566 } else if (arg.equals("-binary")) { 567 _binary = true; 568 _endian = _NATIVE_ENDIAN; 569 continue; 570 } else if (arg.equals("-bigendian")) { 571 _binary = true; 572 _endian = _BIG_ENDIAN; 573 continue; 574 } else if (arg.equals("-littleendian")) { 575 _binary = true; 576 _endian = _LITTLE_ENDIAN; 577 continue; 578 } else if (arg.equals("-db")) { 579 //_debug = 10; 580 continue; 581 } else if (arg.equals("-debug")) { 582 // -debug is not in the original X11 pxgraph. 583 //_debug = (int) Integer.parseInt(args[i++]); 584 continue; 585 } else if (arg.equals("-fg")) { 586 _plot.setForeground(PlotBox.getColorByName(args[i++])); 587 continue; 588 } else if (arg.equals("-help")) { 589 // -help is not in the original X11 pxgraph. 590 //_help(); 591 continue; 592 } else if (arg.equals("-impulses")) { 593 // -impulses is not in the original X11 pxgraph. 594 _plot.setImpulses(true); 595 _plot.setConnected(false); 596 continue; 597 } else if (arg.equals("-lnx")) { 598 _plot.setXLog(true); 599 continue; 600 } else if (arg.equals("-lny")) { 601 _plot.setYLog(true); 602 continue; 603 } else if (arg.equals("-m")) { 604 // -m Markers Marks: various 605 _plot.setMarksStyle("various"); 606 savedmarks = "various"; 607 continue; 608 } else if (arg.equals("-M")) { 609 // -M StyleMarkers Marks: various 610 _plot.setMarksStyle("various"); 611 savedmarks = "various"; 612 continue; 613 } else if (arg.equals("-nl")) { 614 // -nl NoLines Lines: off 615 // If we saw the -bar arg, then assume impulses 616 sawnlarg = true; 617 618 if (sawbararg) { 619 // Restore the _marks in case we did -P -bar -nl 620 _plot.setMarksStyle(savedmarks); 621 _plot.setBars(false); 622 _plot.setImpulses(true); 623 } 624 625 _plot.setConnected(false); 626 continue; 627 } else if (arg.equals("-o")) { 628 // -o <output filename> 629 // _outputFile = args[i++]; 630 i++; 631 continue; 632 } else if (arg.equals("-p")) { 633 // -p PixelMarkers Marks: points 634 _plot.setMarksStyle("points"); 635 savedmarks = "points"; 636 continue; 637 } else if (arg.equals("-P")) { 638 // -P LargePixel Marks: dots\n 639 _plot.setMarksStyle("dots"); 640 savedmarks = "dots"; 641 continue; 642 } else if (arg.equals("-print")) { 643 // -print is not in the original X11 pxgraph. 644 continue; 645 } else if (arg.equals("-rv")) { 646 _plot.setBackground(PlotBox.getColorByName("black")); 647 _plot.setForeground(PlotBox.getColorByName("white")); 648 continue; 649 } else if (arg.equals("-test")) { 650 // -test is not in the original X11 pxgraph. 651 //_test = true; 652 continue; 653 } else if (arg.equals("-tk")) { 654 _plot.setGrid(false); 655 continue; 656 } else if (arg.equals("-v") || arg.equals("-version")) { 657 // -version is not in the original X11 pxgraph. 658 //_version(); 659 continue; 660 } else if (arg.length() > 1 && arg.charAt(0) == '-') { 661 // Process '-<digit> <datasetname>' 662 try { 663 int datasetnumber = Integer.parseInt(arg.substring(1)); 664 if (datasetnumber >= 0) { 665 _plot.addLegend(datasetnumber, args[i++]); 666 continue; 667 } 668 } catch (NumberFormatException e) { 669 } 670 } 671 } else { 672 if (arg.startsWith("=")) { 673 // Process =WxH+X+Y 674 width = Integer 675 .parseInt(arg.substring(1, arg.indexOf('x'))); 676 677 int plusIndex = arg.indexOf('+'); 678 int minusIndex = arg.indexOf('-'); 679 680 if (plusIndex != -1 || minusIndex != -1) { 681 // =WxH+X+Y, =WxH-X+Y, =WxH-X-Y, =WxH+X-Y 682 if (plusIndex != -1 && minusIndex != -1) { 683 // =WxH-X+Y or =WxH+X-Y 684 int index = minusIndex; 685 686 if (plusIndex < minusIndex) { 687 index = plusIndex; 688 } 689 690 height = Integer.parseInt( 691 arg.substring(arg.indexOf('x') + 1, index)); 692 } else { 693 if (plusIndex != -1) { 694 // =WxH+X+Y 695 height = Integer.parseInt(arg.substring( 696 arg.indexOf('x') + 1, plusIndex)); 697 } else { 698 // =WxH-X-Y 699 height = Integer.parseInt(arg.substring( 700 arg.indexOf('x') + 1, minusIndex)); 701 } 702 } 703 } else { 704 if (arg.length() > arg.indexOf('x')) { 705 // =WxH 706 height = Integer.parseInt(arg.substring( 707 arg.indexOf('x') + 1, arg.length())); 708 } 709 } 710 711 // FIXME: it is unclear what X and Y in =WxH+X+Y mean 712 // in a non-toplevel window, so we don't process 713 // those here. See Pxgraph.java for how to process 714 // X and Y for a toplevel window. 715 continue; 716 } 717 } 718 719 if (arg.equals("-")) { 720 sawDash = true; 721 } else { 722 // If we got to here, then we failed to parse the arg 723 throw new CmdLineArgException("Failed to parse `" + arg + "'"); 724 } 725 } 726 727 argumentsRead = i++; 728 729 _plot.setSize(width, height); 730 731 InputStream instream; 732 733 if (sawDash) { 734 // FIXME: Reading from both standard in and files is 735 // not well supported. We always read from standard in first 736 // then from any files. 737 instream = System.in; 738 read(instream); 739 } 740 741 // Findbugs suggests checking for null 742 for (i = argumentsRead; args != null && i < args.length; i++) { 743 // Have a filename. First attempt to open it as a URL. 744 try { 745 URL inurl = new URL(base, args[i]); 746 instream = inurl.openStream(); 747 } catch (MalformedURLException ex) { 748 instream = new FileInputStream(args[i]); 749 } 750 751 read(instream); 752 } 753 754 return argumentsRead; 755 } 756 757 /** Split a string containing pxgraph-compatible command-line arguments 758 * into an array and call parseArgs() on the array. This is used 759 * in the rare circumstance that you want to control the format 760 * of a plot from an applet HTML file rather than in the plot data 761 * file. 762 * @param pxgraphargs The command line arguments. 763 * @param base A base URL for relative URL references, or null if 764 * there is none. 765 * @return The number of arguments read. 766 * @exception CmdLineArgException If there is a problem parsing 767 * the command line arguments. 768 * @exception FileNotFoundException If a file is specified that is not 769 * found. 770 * @exception IOException If an error occurs reading an input file. 771 */ 772 public int parsePxgraphargs(String pxgraphargs, URL base) 773 throws CmdLineArgException, FileNotFoundException, IOException { 774 // We convert the String to a Stream and then use a StreamTokenizer 775 // to parse the arguments into a Vector and then copy 776 // the vector into an array of Strings. We use a Vector 777 // so that we can handle an arbitrary number of arguments 778 Vector argvector = new Vector(); 779 boolean prependdash = false; // true if we need to add a - 780 781 StringReader pin = new StringReader(pxgraphargs); 782 783 try { 784 StreamTokenizer stoken = new StreamTokenizer(pin); 785 786 // We don't want to parse numbers specially, so we reset 787 // the syntax and then add back what we want. 788 stoken.resetSyntax(); 789 stoken.whitespaceChars(0, ' '); 790 stoken.wordChars('(', '~'); 791 stoken.quoteChar('"'); 792 stoken.quoteChar('\''); 793 794 int c; 795 String partialarg = null; 796 out: while (true) { 797 c = stoken.nextToken(); 798 799 //System.out.print(c + " "+stoken.ttype+" "+stoken.sval+" "); 800 switch (stoken.ttype) { // same as value of 'c' 801 case StreamTokenizer.TT_EOF: 802 break out; 803 804 case StreamTokenizer.TT_WORD: 805 806 //System.out.println("Word: " + stoken.sval); 807 if (prependdash) { 808 prependdash = false; 809 810 if (partialarg == null) { 811 argvector.addElement("-" + stoken.sval); 812 } else { 813 argvector 814 .addElement("-" + partialarg + stoken.sval); 815 } 816 } else { 817 if (partialarg == null) { 818 argvector.addElement(stoken.sval); 819 } else { 820 argvector.addElement(partialarg + stoken.sval); 821 } 822 } 823 824 partialarg = null; 825 break; 826 827 case '-': 828 prependdash = true; 829 break; 830 831 case '#': 832 case '$': 833 case '%': 834 case '&': 835 836 // The above chars can be part of a URL. For example 837 // perl scripts use &. However, we cannot include 838 // them in the wordChars() range of chars, since 839 // the single quote is between them and the rest of the 840 // chars. So we have to process them by hand. 841 partialarg = (String) argvector.lastElement() + (char) c; 842 argvector.removeElementAt(argvector.size() - 1); 843 break; 844 845 case '"': 846 case '\'': 847 848 //System.out.println("String: " + stoken.sval); 849 argvector.addElement(stoken.sval); 850 break; 851 852 default: 853 throw new IOException("Failed to parse: '" + (char) c 854 + "' in `" + pxgraphargs + "'"); 855 } 856 } 857 } catch (IOException e) { 858 e.printStackTrace(); 859 } 860 861 // Create a array 862 String[] args = new String[argvector.size()]; 863 864 for (int i = 0; i < argvector.size(); i++) { 865 args[i] = (String) argvector.elementAt(i); 866 } 867 868 return parseArgs(args, base); 869 } 870 871 /** Read a pxgraph-compatible binary or ASCII encoded file. 872 * @param inputStream The input stream. 873 * @exception java.io.IOException If an I/O error occurs. 874 */ 875 public void read(InputStream inputStream) throws IOException { 876 DataInputStream in = new DataInputStream( 877 new BufferedInputStream(inputStream)); 878 879 if (_binary) { 880 int c; 881 float x = 0; 882 float y = 0; 883 float pointCount = 0; 884 boolean byteSwapped = false; 885 boolean connected = false; 886 byte[] input = new byte[4]; 887 888 if (_connected) { 889 connected = true; 890 } 891 892 switch (_endian) { 893 case _NATIVE_ENDIAN: 894 895 try { 896 if (System.getProperty("os.arch").equals("x86")) { 897 byteSwapped = true; 898 } 899 } catch (SecurityException e) { 900 } 901 902 break; 903 904 case _BIG_ENDIAN: 905 break; 906 907 case _LITTLE_ENDIAN: 908 byteSwapped = true; 909 break; 910 911 default: 912 throw new IOException("Internal Error: Don't know about '" 913 + _endian + "' style of endian"); 914 } 915 916 try { 917 // Flag that we are starting a new data set. 918 _firstInSet = true; 919 920 // Flag that we have not seen a DataSet line in this file. 921 _sawFirstDataset = false; 922 923 c = in.readByte(); 924 925 if (c != 'd') { 926 // Assume that the data is one data set, consisting 927 // of 4 byte floats. None of the Ptolemy pxgraph 928 // binary format extensions apply. 929 // Note that the binary format is bigendian, or network 930 // order. Little-endian machines, like x86 will not 931 // be able to write binary data directly 932 // (However, they could use Java's mechanisms for 933 // writing binary files). 934 // Read 3 more bytes, create the x float. 935 int bits = c; 936 bits = bits << 8; 937 bits += in.readByte(); 938 bits = bits << 8; 939 bits += in.readByte(); 940 bits = bits << 8; 941 bits += in.readByte(); 942 943 x = Float.intBitsToFloat(bits); 944 y = in.readFloat(); 945 946 // _addLegendIfNecessary might increment _currentdataset 947 connected = _addLegendIfNecessary(connected); 948 _plot.addPoint(_currentdataset, x, y, connected); 949 950 if (_connected) { 951 connected = true; 952 } 953 954 while (true) { 955 x = in.readFloat(); 956 y = in.readFloat(); 957 connected = _addLegendIfNecessary(connected); 958 _plot.addPoint(_currentdataset, x, y, connected); 959 960 if (_connected) { 961 connected = true; 962 } 963 } 964 } else { 965 // Assume that the data is in the pxgraph binary format. 966 while (true) { 967 // For speed reasons, the Ptolemy group extended 968 // pxgraph to read binary format data. 969 // The format consists of a command character, 970 // followed by optional arguments 971 // d <4byte float> <4byte float> - Draw a X, Y point 972 // e - End of a data set 973 // n <chars> \n - New set name, ends in \n 974 // m - Move to a point 975 switch (c) { 976 case 'd': 977 978 // Data point. 979 if (byteSwapped) { 980 in.readFully(input); 981 x = Float.intBitsToFloat((input[3] & 0xFF) << 24 982 | (input[2] & 0xFF) << 16 983 | (input[1] & 0xFF) << 8 984 | input[0] & 0xFF); 985 in.readFully(input); 986 y = Float.intBitsToFloat((input[3] & 0xFF) << 24 987 | (input[2] & 0xFF) << 16 988 | (input[1] & 0xFF) << 8 989 | input[0] & 0xFF); 990 } else { 991 x = in.readFloat(); 992 y = in.readFloat(); 993 } 994 995 pointCount++; 996 connected = _addLegendIfNecessary(connected); 997 _plot.addPoint(_currentdataset, x, y, connected); 998 999 if (_connected) { 1000 connected = true; 1001 } 1002 1003 break; 1004 1005 case 'e': // End of set name. 1006 case 'm': // a disconnected point 1007 connected = false; 1008 break; 1009 1010 case 'n': 1011 _firstInSet = true; 1012 _sawFirstDataset = true; 1013 1014 StringBuffer datasetname = new StringBuffer(); 1015 _currentdataset++; 1016 1017 // New set name, ends in \n. 1018 while (c != '\n') { 1019 datasetname.append(in.readChar()); 1020 } 1021 1022 _plot.addLegend(_currentdataset, 1023 datasetname.toString()); 1024 _plot.setConnected(true); 1025 break; 1026 1027 default: 1028 throw new IOException("Don't understand `" 1029 + (char) c + "' character " 1030 + "(decimal value = " + c 1031 + ") in binary file. Last point was (" + x 1032 + "," + y + ").\nProcessed " + pointCount 1033 + " points successfully"); 1034 } 1035 1036 c = in.readByte(); 1037 } 1038 } 1039 } catch (EOFException e) { 1040 } 1041 } else { 1042 // Read ASCII files. 1043 // NOTE: These are not in xgraph format, but rather in the 1044 // old ptplot format!! 1045 _plot.read(inputStream); 1046 } 1047 } 1048 1049 /////////////////////////////////////////////////////////////////// 1050 //// protected members //// 1051 1052 /** The current dataset, used for handling multiple files. */ 1053 protected int _currentdataset = -1; 1054 1055 /** The plot object to which to apply commands. */ 1056 protected Plot _plot; 1057 1058 /////////////////////////////////////////////////////////////////// 1059 //// private methods //// 1060 // Add a legend if necessary, return the value of the connected flag. 1061 private boolean _addLegendIfNecessary(boolean connected) { 1062 if (!_sawFirstDataset || _currentdataset < 0) { 1063 // We did not set a DataSet line, but 1064 // we did get called with -<digit> args 1065 _sawFirstDataset = true; 1066 _currentdataset++; 1067 } 1068 1069 if (_plot.getLegend(_currentdataset) == null) { 1070 // We did not see a "DataSet" string yet, 1071 // nor did we call addLegend(). 1072 _firstInSet = true; 1073 _sawFirstDataset = true; 1074 _plot.addLegend(_currentdataset, "Set " + _currentdataset); 1075 } 1076 1077 if (_firstInSet) { 1078 connected = false; 1079 _firstInSet = false; 1080 } 1081 1082 return connected; 1083 } 1084 1085 // Parse a string with a comma into two doubles. 1086 // If there is no comma, return a single double. 1087 private double[] _parseDoubles(String spec) { 1088 int comma = spec.indexOf(","); 1089 1090 if (comma < 0) { 1091 double[] result = new double[1]; 1092 result[0] = Double.parseDouble(spec); 1093 return result; 1094 } else { 1095 double[] result = new double[2]; 1096 String spec1 = spec.substring(0, comma); 1097 result[0] = Double.parseDouble(spec1); 1098 1099 String spec2 = spec.substring(comma + 1); 1100 result[1] = Double.parseDouble(spec2); 1101 return result; 1102 } 1103 } 1104 1105 /////////////////////////////////////////////////////////////////// 1106 //// private members //// 1107 // Check the osarch and use the appropriate endian. 1108 private static final int _NATIVE_ENDIAN = 0; 1109 1110 // Data is in big-endian 1111 private static final int _BIG_ENDIAN = 1; 1112 1113 // Data is in little-endian 1114 private static final int _LITTLE_ENDIAN = 2; 1115 1116 // Flag indicating whether the command specified that the format 1117 private boolean _binary = false; 1118 1119 private boolean _connected = true; 1120 1121 // For debugging, call with -db or -debug. 1122 //private static int _debug = 0; 1123 1124 /** @serial Format to read data in. */ 1125 private int _endian = _NATIVE_ENDIAN; 1126 1127 // Is this the first datapoint in a set? 1128 private boolean _firstInSet = true; 1129 1130 // Have we seen a DataSet line in the current data file? 1131 private boolean _sawFirstDataset = false; 1132}