001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------- 028 * SymbolAxis.java 029 * --------------- 030 * (C) Copyright 2002-2007, by Anthony Boulestreau and Contributors. 031 * 032 * Original Author: Anthony Boulestreau; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * 036 * Changes 037 * ------- 038 * 29-Mar-2002 : First version (AB); 039 * 19-Apr-2002 : Updated formatting and import statements (DG); 040 * 21-Jun-2002 : Make change to use the class TickUnit - remove valueToString() 041 * method and add SymbolicTickUnit (AB); 042 * 25-Jun-2002 : Removed redundant code (DG); 043 * 25-Jul-2002 : Changed order of parameters in ValueAxis constructor (DG); 044 * 05-Sep-2002 : Updated constructor to reflect changes in the Axis class (DG); 045 * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG); 046 * 14-Feb-2003 : Added back missing constructor code (DG); 047 * 26-Mar-2003 : Implemented Serializable (DG); 048 * 14-May-2003 : Renamed HorizontalSymbolicAxis --> SymbolicAxis and merged in 049 * VerticalSymbolicAxis (DG); 050 * 12-Aug-2003 : Fixed bug where refreshTicks() method has different signature 051 * to super class (DG); 052 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 053 * 02-Nov-2003 : Added code to avoid overlapping labels (MR); 054 * 07-Nov-2003 : Modified to use new tick classes (DG); 055 * 18-Nov-2003 : Fixed bug where symbols are not being displayed on the 056 * axis (DG); 057 * 24-Nov-2003 : Added fix for gridlines on zooming (bug id 834643) (DG); 058 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 059 * 11-Mar-2004 : Modified the way the background grid color is being drawn, see 060 * this thread: 061 * http://www.jfree.org/phpBB2/viewtopic.php?p=22973 (DG); 062 * 16-Mar-2004 : Added plotState to draw() method (DG); 063 * 07-Apr-2004 : Modified string bounds calculation (DG); 064 * 28-Mar-2005 : Renamed autoRangeIncludesZero() --> getAutoRangeIncludesZero() 065 * and autoRangeStickyZero() --> getAutoRangeStickyZero() (DG); 066 * 05-Jul-2005 : Fixed signature on refreshTicks() method - see bug report 067 * 1232264 (DG); 068 * 06-Jul-2005 : Renamed SymbolicAxis --> SymbolAxis, added equals() method, 069 * renamed getSymbolicValue() --> getSymbols(), renamed 070 * symbolicGridPaint --> gridBandPaint, fixed serialization of 071 * gridBandPaint, renamed symbolicGridLinesVisible --> 072 * gridBandsVisible, eliminated symbolicGridLineList (DG); 073 * ------------- JFREECHART 1.0.x --------------------------------------------- 074 * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG); 075 * 28-Feb-2007 : Fixed bug 1669302 (tick label overlap) (DG); 076 * 077 */ 078 079 package org.jfree.chart.axis; 080 081 import java.awt.BasicStroke; 082 import java.awt.Color; 083 import java.awt.Font; 084 import java.awt.Graphics2D; 085 import java.awt.Paint; 086 import java.awt.Shape; 087 import java.awt.Stroke; 088 import java.awt.geom.Rectangle2D; 089 import java.io.IOException; 090 import java.io.ObjectInputStream; 091 import java.io.ObjectOutputStream; 092 import java.io.Serializable; 093 import java.text.NumberFormat; 094 import java.util.Arrays; 095 import java.util.Iterator; 096 import java.util.List; 097 098 import org.jfree.chart.event.AxisChangeEvent; 099 import org.jfree.chart.plot.Plot; 100 import org.jfree.chart.plot.PlotRenderingInfo; 101 import org.jfree.chart.plot.ValueAxisPlot; 102 import org.jfree.data.Range; 103 import org.jfree.io.SerialUtilities; 104 import org.jfree.text.TextUtilities; 105 import org.jfree.ui.RectangleEdge; 106 import org.jfree.ui.TextAnchor; 107 import org.jfree.util.PaintUtilities; 108 109 /** 110 * A standard linear value axis that replaces integer values with symbols. 111 */ 112 public class SymbolAxis extends NumberAxis implements Serializable { 113 114 /** For serialization. */ 115 private static final long serialVersionUID = 7216330468770619716L; 116 117 /** The default grid band paint. */ 118 public static final Paint DEFAULT_GRID_BAND_PAINT 119 = new Color(232, 234, 232, 128); 120 121 /** The list of symbols to display instead of the numeric values. */ 122 private List symbols; 123 124 /** The paint used to color the grid bands (if the bands are visible). */ 125 private transient Paint gridBandPaint; 126 127 /** Flag that indicates whether or not grid bands are visible. */ 128 private boolean gridBandsVisible; 129 130 /** 131 * Constructs a symbol axis, using default attribute values where 132 * necessary. 133 * 134 * @param label the axis label (<code>null</code> permitted). 135 * @param sv the list of symbols to display instead of the numeric 136 * values. 137 */ 138 public SymbolAxis(String label, String[] sv) { 139 super(label); 140 this.symbols = Arrays.asList(sv); 141 this.gridBandsVisible = true; 142 this.gridBandPaint = DEFAULT_GRID_BAND_PAINT; 143 144 setAutoTickUnitSelection(false, false); 145 setAutoRangeStickyZero(false); 146 147 } 148 149 /** 150 * Returns an array of the symbols for the axis. 151 * 152 * @return The symbols. 153 */ 154 public String[] getSymbols() { 155 String[] result = new String[this.symbols.size()]; 156 result = (String[]) this.symbols.toArray(result); 157 return result; 158 } 159 160 /** 161 * Returns the paint used to color the grid bands. 162 * 163 * @return The grid band paint (never <code>null</code>). 164 * 165 * @see #setGridBandPaint(Paint) 166 * @see #isGridBandsVisible() 167 */ 168 public Paint getGridBandPaint() { 169 return this.gridBandPaint; 170 } 171 172 /** 173 * Sets the grid band paint and sends an {@link AxisChangeEvent} to 174 * all registered listeners. 175 * 176 * @param paint the paint (<code>null</code> not permitted). 177 * 178 * @see #getGridBandPaint() 179 */ 180 public void setGridBandPaint(Paint paint) { 181 if (paint == null) { 182 throw new IllegalArgumentException("Null 'paint' argument."); 183 } 184 this.gridBandPaint = paint; 185 notifyListeners(new AxisChangeEvent(this)); 186 } 187 188 /** 189 * Returns <code>true</code> if the grid bands are showing, and 190 * <code>false</code> otherwise. 191 * 192 * @return <code>true</code> if the grid bands are showing, and 193 * <code>false</code> otherwise. 194 * 195 * @see #setGridBandsVisible(boolean) 196 */ 197 public boolean isGridBandsVisible() { 198 return this.gridBandsVisible; 199 } 200 201 /** 202 * Sets the visibility of the grid bands and notifies registered 203 * listeners that the axis has been modified. 204 * 205 * @param flag the new setting. 206 * 207 * @see #isGridBandsVisible() 208 */ 209 public void setGridBandsVisible(boolean flag) { 210 if (this.gridBandsVisible != flag) { 211 this.gridBandsVisible = flag; 212 notifyListeners(new AxisChangeEvent(this)); 213 } 214 } 215 216 /** 217 * This operation is not supported by this axis. 218 * 219 * @param g2 the graphics device. 220 * @param dataArea the area in which the plot and axes should be drawn. 221 * @param edge the edge along which the axis is drawn. 222 */ 223 protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, 224 RectangleEdge edge) { 225 throw new UnsupportedOperationException(); 226 } 227 228 /** 229 * Draws the axis on a Java 2D graphics device (such as the screen or a 230 * printer). 231 * 232 * @param g2 the graphics device (<code>null</code> not permitted). 233 * @param cursor the cursor location. 234 * @param plotArea the area within which the plot and axes should be drawn 235 * (<code>null</code> not permitted). 236 * @param dataArea the area within which the data should be drawn 237 * (<code>null</code> not permitted). 238 * @param edge the axis location (<code>null</code> not permitted). 239 * @param plotState collects information about the plot 240 * (<code>null</code> permitted). 241 * 242 * @return The axis state (never <code>null</code>). 243 */ 244 public AxisState draw(Graphics2D g2, 245 double cursor, 246 Rectangle2D plotArea, 247 Rectangle2D dataArea, 248 RectangleEdge edge, 249 PlotRenderingInfo plotState) { 250 251 AxisState info = new AxisState(cursor); 252 if (isVisible()) { 253 info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState); 254 } 255 if (this.gridBandsVisible) { 256 drawGridBands(g2, plotArea, dataArea, edge, info.getTicks()); 257 } 258 return info; 259 260 } 261 262 /** 263 * Draws the grid bands. Alternate bands are colored using 264 * <CODE>gridBandPaint<CODE> (<CODE>DEFAULT_GRID_BAND_PAINT</CODE> by 265 * default). 266 * 267 * @param g2 the graphics device. 268 * @param plotArea the area within which the chart should be drawn. 269 * @param dataArea the area within which the plot should be drawn (a 270 * subset of the drawArea). 271 * @param edge the axis location. 272 * @param ticks the ticks. 273 */ 274 protected void drawGridBands(Graphics2D g2, 275 Rectangle2D plotArea, 276 Rectangle2D dataArea, 277 RectangleEdge edge, 278 List ticks) { 279 280 Shape savedClip = g2.getClip(); 281 g2.clip(dataArea); 282 if (RectangleEdge.isTopOrBottom(edge)) { 283 drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks); 284 } 285 else if (RectangleEdge.isLeftOrRight(edge)) { 286 drawGridBandsVertical(g2, plotArea, dataArea, true, ticks); 287 } 288 g2.setClip(savedClip); 289 290 } 291 292 /** 293 * Draws the grid bands for the axis when it is at the top or bottom of 294 * the plot. 295 * 296 * @param g2 the graphics device. 297 * @param plotArea the area within which the chart should be drawn. 298 * @param dataArea the area within which the plot should be drawn 299 * (a subset of the drawArea). 300 * @param firstGridBandIsDark True: the first grid band takes the 301 * color of <CODE>gridBandPaint<CODE>. 302 * False: the second grid band takes the 303 * color of <CODE>gridBandPaint<CODE>. 304 * @param ticks the ticks. 305 */ 306 protected void drawGridBandsHorizontal(Graphics2D g2, 307 Rectangle2D plotArea, 308 Rectangle2D dataArea, 309 boolean firstGridBandIsDark, 310 List ticks) { 311 312 boolean currentGridBandIsDark = firstGridBandIsDark; 313 double yy = dataArea.getY(); 314 double xx1, xx2; 315 316 //gets the outline stroke width of the plot 317 double outlineStrokeWidth; 318 if (getPlot().getOutlineStroke() != null) { 319 outlineStrokeWidth 320 = ((BasicStroke) getPlot().getOutlineStroke()).getLineWidth(); 321 } 322 else { 323 outlineStrokeWidth = 1d; 324 } 325 326 Iterator iterator = ticks.iterator(); 327 ValueTick tick; 328 Rectangle2D band; 329 while (iterator.hasNext()) { 330 tick = (ValueTick) iterator.next(); 331 xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea, 332 RectangleEdge.BOTTOM); 333 xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea, 334 RectangleEdge.BOTTOM); 335 if (currentGridBandIsDark) { 336 g2.setPaint(this.gridBandPaint); 337 } 338 else { 339 g2.setPaint(Color.white); 340 } 341 band = new Rectangle2D.Double(xx1, yy + outlineStrokeWidth, 342 xx2 - xx1, dataArea.getMaxY() - yy - outlineStrokeWidth); 343 g2.fill(band); 344 currentGridBandIsDark = !currentGridBandIsDark; 345 } 346 g2.setPaintMode(); 347 } 348 349 /** 350 * Draws the grid bands for the axis when it is at the top or bottom of 351 * the plot. 352 * 353 * @param g2 the graphics device. 354 * @param drawArea the area within which the chart should be drawn. 355 * @param plotArea the area within which the plot should be drawn (a 356 * subset of the drawArea). 357 * @param firstGridBandIsDark True: the first grid band takes the 358 * color of <CODE>gridBandPaint<CODE>. 359 * False: the second grid band takes the 360 * color of <CODE>gridBandPaint<CODE>. 361 * @param ticks a list of ticks. 362 */ 363 protected void drawGridBandsVertical(Graphics2D g2, 364 Rectangle2D drawArea, 365 Rectangle2D plotArea, 366 boolean firstGridBandIsDark, 367 List ticks) { 368 369 boolean currentGridBandIsDark = firstGridBandIsDark; 370 double xx = plotArea.getX(); 371 double yy1, yy2; 372 373 //gets the outline stroke width of the plot 374 double outlineStrokeWidth; 375 Stroke outlineStroke = getPlot().getOutlineStroke(); 376 if (outlineStroke != null && outlineStroke instanceof BasicStroke) { 377 outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth(); 378 } 379 else { 380 outlineStrokeWidth = 1d; 381 } 382 383 Iterator iterator = ticks.iterator(); 384 ValueTick tick; 385 Rectangle2D band; 386 while (iterator.hasNext()) { 387 tick = (ValueTick) iterator.next(); 388 yy1 = valueToJava2D(tick.getValue() + 0.5d, plotArea, 389 RectangleEdge.LEFT); 390 yy2 = valueToJava2D(tick.getValue() - 0.5d, plotArea, 391 RectangleEdge.LEFT); 392 if (currentGridBandIsDark) { 393 g2.setPaint(this.gridBandPaint); 394 } 395 else { 396 g2.setPaint(Color.white); 397 } 398 band = new Rectangle2D.Double(xx + outlineStrokeWidth, yy1, 399 plotArea.getMaxX() - xx - outlineStrokeWidth, yy2 - yy1); 400 g2.fill(band); 401 currentGridBandIsDark = !currentGridBandIsDark; 402 } 403 g2.setPaintMode(); 404 } 405 406 /** 407 * Rescales the axis to ensure that all data is visible. 408 */ 409 protected void autoAdjustRange() { 410 411 Plot plot = getPlot(); 412 if (plot == null) { 413 return; // no plot, no data 414 } 415 416 if (plot instanceof ValueAxisPlot) { 417 418 // ensure that all the symbols are displayed 419 double upper = this.symbols.size() - 1; 420 double lower = 0; 421 double range = upper - lower; 422 423 // ensure the autorange is at least <minRange> in size... 424 double minRange = getAutoRangeMinimumSize(); 425 if (range < minRange) { 426 upper = (upper + lower + minRange) / 2; 427 lower = (upper + lower - minRange) / 2; 428 } 429 430 // this ensure that the grid bands will be displayed correctly. 431 double upperMargin = 0.5; 432 double lowerMargin = 0.5; 433 434 if (getAutoRangeIncludesZero()) { 435 if (getAutoRangeStickyZero()) { 436 if (upper <= 0.0) { 437 upper = 0.0; 438 } 439 else { 440 upper = upper + upperMargin; 441 } 442 if (lower >= 0.0) { 443 lower = 0.0; 444 } 445 else { 446 lower = lower - lowerMargin; 447 } 448 } 449 else { 450 upper = Math.max(0.0, upper + upperMargin); 451 lower = Math.min(0.0, lower - lowerMargin); 452 } 453 } 454 else { 455 if (getAutoRangeStickyZero()) { 456 if (upper <= 0.0) { 457 upper = Math.min(0.0, upper + upperMargin); 458 } 459 else { 460 upper = upper + upperMargin * range; 461 } 462 if (lower >= 0.0) { 463 lower = Math.max(0.0, lower - lowerMargin); 464 } 465 else { 466 lower = lower - lowerMargin; 467 } 468 } 469 else { 470 upper = upper + upperMargin; 471 lower = lower - lowerMargin; 472 } 473 } 474 475 setRange(new Range(lower, upper), false, false); 476 477 } 478 479 } 480 481 /** 482 * Calculates the positions of the tick labels for the axis, storing the 483 * results in the tick label list (ready for drawing). 484 * 485 * @param g2 the graphics device. 486 * @param state the axis state. 487 * @param dataArea the area in which the data should be drawn. 488 * @param edge the location of the axis. 489 * 490 * @return A list of ticks. 491 */ 492 public List refreshTicks(Graphics2D g2, 493 AxisState state, 494 Rectangle2D dataArea, 495 RectangleEdge edge) { 496 List ticks = null; 497 if (RectangleEdge.isTopOrBottom(edge)) { 498 ticks = refreshTicksHorizontal(g2, dataArea, edge); 499 } 500 else if (RectangleEdge.isLeftOrRight(edge)) { 501 ticks = refreshTicksVertical(g2, dataArea, edge); 502 } 503 return ticks; 504 } 505 506 /** 507 * Calculates the positions of the tick labels for the axis, storing the 508 * results in the tick label list (ready for drawing). 509 * 510 * @param g2 the graphics device. 511 * @param dataArea the area in which the data should be drawn. 512 * @param edge the location of the axis. 513 * 514 * @return The ticks. 515 */ 516 protected List refreshTicksHorizontal(Graphics2D g2, 517 Rectangle2D dataArea, 518 RectangleEdge edge) { 519 520 List ticks = new java.util.ArrayList(); 521 522 Font tickLabelFont = getTickLabelFont(); 523 g2.setFont(tickLabelFont); 524 525 double size = getTickUnit().getSize(); 526 int count = calculateVisibleTickCount(); 527 double lowestTickValue = calculateLowestVisibleTickValue(); 528 529 double previousDrawnTickLabelPos = 0.0; 530 double previousDrawnTickLabelLength = 0.0; 531 532 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 533 for (int i = 0; i < count; i++) { 534 double currentTickValue = lowestTickValue + (i * size); 535 double xx = valueToJava2D(currentTickValue, dataArea, edge); 536 String tickLabel; 537 NumberFormat formatter = getNumberFormatOverride(); 538 if (formatter != null) { 539 tickLabel = formatter.format(currentTickValue); 540 } 541 else { 542 tickLabel = valueToString(currentTickValue); 543 } 544 545 // avoid to draw overlapping tick labels 546 Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2, 547 g2.getFontMetrics()); 548 double tickLabelLength = isVerticalTickLabels() 549 ? bounds.getHeight() : bounds.getWidth(); 550 boolean tickLabelsOverlapping = false; 551 if (i > 0) { 552 double avgTickLabelLength = (previousDrawnTickLabelLength 553 + tickLabelLength) / 2.0; 554 if (Math.abs(xx - previousDrawnTickLabelPos) 555 < avgTickLabelLength) { 556 tickLabelsOverlapping = true; 557 } 558 } 559 if (tickLabelsOverlapping) { 560 tickLabel = ""; // don't draw this tick label 561 } 562 else { 563 // remember these values for next comparison 564 previousDrawnTickLabelPos = xx; 565 previousDrawnTickLabelLength = tickLabelLength; 566 } 567 568 TextAnchor anchor = null; 569 TextAnchor rotationAnchor = null; 570 double angle = 0.0; 571 if (isVerticalTickLabels()) { 572 anchor = TextAnchor.CENTER_RIGHT; 573 rotationAnchor = TextAnchor.CENTER_RIGHT; 574 if (edge == RectangleEdge.TOP) { 575 angle = Math.PI / 2.0; 576 } 577 else { 578 angle = -Math.PI / 2.0; 579 } 580 } 581 else { 582 if (edge == RectangleEdge.TOP) { 583 anchor = TextAnchor.BOTTOM_CENTER; 584 rotationAnchor = TextAnchor.BOTTOM_CENTER; 585 } 586 else { 587 anchor = TextAnchor.TOP_CENTER; 588 rotationAnchor = TextAnchor.TOP_CENTER; 589 } 590 } 591 Tick tick = new NumberTick(new Double(currentTickValue), 592 tickLabel, anchor, rotationAnchor, angle); 593 ticks.add(tick); 594 } 595 } 596 return ticks; 597 598 } 599 600 /** 601 * Calculates the positions of the tick labels for the axis, storing the 602 * results in the tick label list (ready for drawing). 603 * 604 * @param g2 the graphics device. 605 * @param dataArea the area in which the plot should be drawn. 606 * @param edge the location of the axis. 607 * 608 * @return The ticks. 609 */ 610 protected List refreshTicksVertical(Graphics2D g2, 611 Rectangle2D dataArea, 612 RectangleEdge edge) { 613 614 List ticks = new java.util.ArrayList(); 615 616 Font tickLabelFont = getTickLabelFont(); 617 g2.setFont(tickLabelFont); 618 619 double size = getTickUnit().getSize(); 620 int count = calculateVisibleTickCount(); 621 double lowestTickValue = calculateLowestVisibleTickValue(); 622 623 double previousDrawnTickLabelPos = 0.0; 624 double previousDrawnTickLabelLength = 0.0; 625 626 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 627 for (int i = 0; i < count; i++) { 628 double currentTickValue = lowestTickValue + (i * size); 629 double yy = valueToJava2D(currentTickValue, dataArea, edge); 630 String tickLabel; 631 NumberFormat formatter = getNumberFormatOverride(); 632 if (formatter != null) { 633 tickLabel = formatter.format(currentTickValue); 634 } 635 else { 636 tickLabel = valueToString(currentTickValue); 637 } 638 639 // avoid to draw overlapping tick labels 640 Rectangle2D bounds = TextUtilities.getTextBounds(tickLabel, g2, 641 g2.getFontMetrics()); 642 double tickLabelLength = isVerticalTickLabels() 643 ? bounds.getWidth() : bounds.getHeight(); 644 boolean tickLabelsOverlapping = false; 645 if (i > 0) { 646 double avgTickLabelLength = (previousDrawnTickLabelLength 647 + tickLabelLength) / 2.0; 648 if (Math.abs(yy - previousDrawnTickLabelPos) 649 < avgTickLabelLength) { 650 tickLabelsOverlapping = true; 651 } 652 } 653 if (tickLabelsOverlapping) { 654 tickLabel = ""; // don't draw this tick label 655 } 656 else { 657 // remember these values for next comparison 658 previousDrawnTickLabelPos = yy; 659 previousDrawnTickLabelLength = tickLabelLength; 660 } 661 662 TextAnchor anchor = null; 663 TextAnchor rotationAnchor = null; 664 double angle = 0.0; 665 if (isVerticalTickLabels()) { 666 anchor = TextAnchor.BOTTOM_CENTER; 667 rotationAnchor = TextAnchor.BOTTOM_CENTER; 668 if (edge == RectangleEdge.LEFT) { 669 angle = -Math.PI / 2.0; 670 } 671 else { 672 angle = Math.PI / 2.0; 673 } 674 } 675 else { 676 if (edge == RectangleEdge.LEFT) { 677 anchor = TextAnchor.CENTER_RIGHT; 678 rotationAnchor = TextAnchor.CENTER_RIGHT; 679 } 680 else { 681 anchor = TextAnchor.CENTER_LEFT; 682 rotationAnchor = TextAnchor.CENTER_LEFT; 683 } 684 } 685 Tick tick = new NumberTick(new Double(currentTickValue), 686 tickLabel, anchor, rotationAnchor, angle); 687 ticks.add(tick); 688 } 689 } 690 return ticks; 691 692 } 693 694 /** 695 * Converts a value to a string, using the list of symbols. 696 * 697 * @param value value to convert. 698 * 699 * @return The symbol. 700 */ 701 public String valueToString(double value) { 702 String strToReturn; 703 try { 704 strToReturn = (String) this.symbols.get((int) value); 705 } 706 catch (IndexOutOfBoundsException ex) { 707 strToReturn = ""; 708 } 709 return strToReturn; 710 } 711 712 /** 713 * Tests this axis for equality with an arbitrary object. 714 * 715 * @param obj the object (<code>null</code> permitted). 716 * 717 * @return A boolean. 718 */ 719 public boolean equals(Object obj) { 720 if (obj == this) { 721 return true; 722 } 723 if (!(obj instanceof SymbolAxis)) { 724 return false; 725 } 726 SymbolAxis that = (SymbolAxis) obj; 727 if (!this.symbols.equals(that.symbols)) { 728 return false; 729 } 730 if (this.gridBandsVisible != that.gridBandsVisible) { 731 return false; 732 } 733 if (!PaintUtilities.equal(this.gridBandPaint, that.gridBandPaint)) { 734 return false; 735 } 736 return super.equals(obj); 737 } 738 739 /** 740 * Provides serialization support. 741 * 742 * @param stream the output stream. 743 * 744 * @throws IOException if there is an I/O error. 745 */ 746 private void writeObject(ObjectOutputStream stream) throws IOException { 747 stream.defaultWriteObject(); 748 SerialUtilities.writePaint(this.gridBandPaint, stream); 749 } 750 751 /** 752 * Provides serialization support. 753 * 754 * @param stream the input stream. 755 * 756 * @throws IOException if there is an I/O error. 757 * @throws ClassNotFoundException if there is a classpath problem. 758 */ 759 private void readObject(ObjectInputStream stream) 760 throws IOException, ClassNotFoundException { 761 stream.defaultReadObject(); 762 this.gridBandPaint = SerialUtilities.readPaint(stream); 763 } 764 765 }