001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------- 028 * SymbolAxis.java 029 * --------------- 030 * (C) Copyright 2002-present, by Anthony Boulestreau and Contributors. 031 * 032 * Original Author: Anthony Boulestreau; 033 * Contributor(s): David Gilbert; 034 * 035 */ 036 037package org.jfree.chart.axis; 038 039import java.awt.BasicStroke; 040import java.awt.Color; 041import java.awt.Font; 042import java.awt.Graphics2D; 043import java.awt.Paint; 044import java.awt.Shape; 045import java.awt.Stroke; 046import java.awt.geom.Rectangle2D; 047import java.io.IOException; 048import java.io.ObjectInputStream; 049import java.io.ObjectOutputStream; 050import java.io.Serializable; 051import java.text.NumberFormat; 052import java.util.Arrays; 053import java.util.Iterator; 054import java.util.List; 055 056import org.jfree.chart.plot.Plot; 057import org.jfree.chart.plot.PlotRenderingInfo; 058import org.jfree.chart.plot.ValueAxisPlot; 059import org.jfree.chart.text.TextUtils; 060import org.jfree.chart.ui.RectangleEdge; 061import org.jfree.chart.ui.TextAnchor; 062import org.jfree.chart.util.PaintUtils; 063import org.jfree.chart.util.Args; 064import org.jfree.chart.util.SerialUtils; 065import org.jfree.data.Range; 066 067/** 068 * A standard linear value axis that replaces integer values with symbols. 069 */ 070public class SymbolAxis extends NumberAxis implements Serializable { 071 072 /** For serialization. */ 073 private static final long serialVersionUID = 7216330468770619716L; 074 075 /** The default grid band paint. */ 076 public static final Paint DEFAULT_GRID_BAND_PAINT 077 = new Color(232, 234, 232, 128); 078 079 /** 080 * The default paint for alternate grid bands. 081 */ 082 public static final Paint DEFAULT_GRID_BAND_ALTERNATE_PAINT 083 = new Color(0, 0, 0, 0); // transparent 084 085 /** The list of symbols to display instead of the numeric values. */ 086 private List symbols; 087 088 /** Flag that indicates whether or not grid bands are visible. */ 089 private boolean gridBandsVisible; 090 091 /** The paint used to color the grid bands (if the bands are visible). */ 092 private transient Paint gridBandPaint; 093 094 /** 095 * The paint used to fill the alternate grid bands. 096 */ 097 private transient Paint gridBandAlternatePaint; 098 099 /** 100 * Constructs a symbol axis, using default attribute values where 101 * necessary. 102 * 103 * @param label the axis label ({@code null} permitted). 104 * @param sv the list of symbols to display instead of the numeric 105 * values. 106 */ 107 public SymbolAxis(String label, String[] sv) { 108 super(label); 109 this.symbols = Arrays.asList(sv); 110 this.gridBandsVisible = true; 111 this.gridBandPaint = DEFAULT_GRID_BAND_PAINT; 112 this.gridBandAlternatePaint = DEFAULT_GRID_BAND_ALTERNATE_PAINT; 113 setAutoTickUnitSelection(false, false); 114 setAutoRangeStickyZero(false); 115 } 116 117 /** 118 * Returns an array of the symbols for the axis. 119 * 120 * @return The symbols. 121 */ 122 public String[] getSymbols() { 123 String[] result = new String[this.symbols.size()]; 124 result = (String[]) this.symbols.toArray(result); 125 return result; 126 } 127 128 /** 129 * Returns the flag that controls whether or not grid bands are drawn for 130 * the axis. The default value is {@code true}. 131 * 132 * @return A boolean. 133 * 134 * @see #setGridBandsVisible(boolean) 135 */ 136 public boolean isGridBandsVisible() { 137 return this.gridBandsVisible; 138 } 139 140 /** 141 * Sets the flag that controls whether or not grid bands are drawn for this 142 * axis and notifies registered listeners that the axis has been modified. 143 * Each band is the area between two adjacent gridlines 144 * running perpendicular to the axis. When the bands are drawn they are 145 * filled with the colors {@link #getGridBandPaint()} and 146 * {@link #getGridBandAlternatePaint()} in an alternating sequence. 147 * 148 * @param flag the new setting. 149 * 150 * @see #isGridBandsVisible() 151 */ 152 public void setGridBandsVisible(boolean flag) { 153 this.gridBandsVisible = flag; 154 fireChangeEvent(); 155 } 156 157 /** 158 * Returns the paint used to color grid bands (two colors are used 159 * alternately, the other is returned by 160 * {@link #getGridBandAlternatePaint()}). The default value is 161 * {@link #DEFAULT_GRID_BAND_PAINT}. 162 * 163 * @return The paint (never {@code null}). 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 notifies registered listeners that the 174 * axis has been changed. See the {@link #setGridBandsVisible(boolean)} 175 * method for more information about grid bands. 176 * 177 * @param paint the paint ({@code null} not permitted). 178 * 179 * @see #getGridBandPaint() 180 */ 181 public void setGridBandPaint(Paint paint) { 182 Args.nullNotPermitted(paint, "paint"); 183 this.gridBandPaint = paint; 184 fireChangeEvent(); 185 } 186 187 /** 188 * Returns the second paint used to color grid bands (two colors are used 189 * alternately, the other is returned by {@link #getGridBandPaint()}). 190 * The default value is {@link #DEFAULT_GRID_BAND_ALTERNATE_PAINT} 191 * (transparent). 192 * 193 * @return The paint (never {@code null}). 194 * 195 * @see #setGridBandAlternatePaint(Paint) 196 */ 197 public Paint getGridBandAlternatePaint() { 198 return this.gridBandAlternatePaint; 199 } 200 201 /** 202 * Sets the grid band paint and notifies registered listeners that the 203 * axis has been changed. See the {@link #setGridBandsVisible(boolean)} 204 * method for more information about grid bands. 205 * 206 * @param paint the paint ({@code null} not permitted). 207 * 208 * @see #getGridBandAlternatePaint() 209 * @see #setGridBandPaint(Paint) 210 */ 211 public void setGridBandAlternatePaint(Paint paint) { 212 Args.nullNotPermitted(paint, "paint"); 213 this.gridBandAlternatePaint = paint; 214 fireChangeEvent(); 215 } 216 217 /** 218 * This operation is not supported by this axis. 219 * 220 * @param g2 the graphics device. 221 * @param dataArea the area in which the plot and axes should be drawn. 222 * @param edge the edge along which the axis is drawn. 223 */ 224 @Override 225 protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, 226 RectangleEdge edge) { 227 throw new UnsupportedOperationException(); 228 } 229 230 /** 231 * Draws the axis on a Java 2D graphics device (such as the screen or a 232 * printer). 233 * 234 * @param g2 the graphics device ({@code null} not permitted). 235 * @param cursor the cursor location. 236 * @param plotArea the area within which the plot and axes should be drawn 237 * ({@code null} not permitted). 238 * @param dataArea the area within which the data should be drawn 239 * ({@code null} not permitted). 240 * @param edge the axis location ({@code null} not permitted). 241 * @param plotState collects information about the plot 242 * ({@code null} permitted). 243 * 244 * @return The axis state (never {@code null}). 245 */ 246 @Override 247 public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, 248 Rectangle2D dataArea, 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 * {@link #getGridBandPaint()} and {@link #getGridBandAlternatePaint()}. 265 * 266 * @param g2 the graphics target ({@code null} not permitted). 267 * @param plotArea the area within which the plot is drawn 268 * ({@code null} not permitted). 269 * @param dataArea the data area to which the axes are aligned 270 * ({@code null} not permitted). 271 * @param edge the edge to which the axis is aligned ({@code null} not 272 * permitted). 273 * @param ticks the ticks ({@code null} not permitted). 274 */ 275 protected void drawGridBands(Graphics2D g2, Rectangle2D plotArea, 276 Rectangle2D dataArea, RectangleEdge edge, List ticks) { 277 Shape savedClip = g2.getClip(); 278 g2.clip(dataArea); 279 if (RectangleEdge.isTopOrBottom(edge)) { 280 drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks); 281 } else if (RectangleEdge.isLeftOrRight(edge)) { 282 drawGridBandsVertical(g2, plotArea, dataArea, true, ticks); 283 } 284 g2.setClip(savedClip); 285 } 286 287 /** 288 * Draws the grid bands for the axis when it is at the top or bottom of 289 * the plot. 290 * 291 * @param g2 the graphics target ({@code null} not permitted). 292 * @param plotArea the area within which the plot is drawn (not used here). 293 * @param dataArea the area for the data (to which the axes are aligned, 294 * {@code null} not permitted). 295 * @param firstGridBandIsDark True: the first grid band takes the 296 * color of {@code gridBandPaint}. 297 * False: the second grid band takes the 298 * color of {@code gridBandPaint}. 299 * @param ticks a list of ticks ({@code null} not permitted). 300 */ 301 protected void drawGridBandsHorizontal(Graphics2D g2, 302 Rectangle2D plotArea, Rectangle2D dataArea, 303 boolean firstGridBandIsDark, List ticks) { 304 305 boolean currentGridBandIsDark = firstGridBandIsDark; 306 double yy = dataArea.getY(); 307 double xx1, xx2; 308 309 //gets the outline stroke width of the plot 310 double outlineStrokeWidth = 1.0; 311 Stroke outlineStroke = getPlot().getOutlineStroke(); 312 if (outlineStroke != null && outlineStroke instanceof BasicStroke) { 313 outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth(); 314 } 315 316 Iterator iterator = ticks.iterator(); 317 ValueTick tick; 318 Rectangle2D band; 319 while (iterator.hasNext()) { 320 tick = (ValueTick) iterator.next(); 321 xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea, 322 RectangleEdge.BOTTOM); 323 xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea, 324 RectangleEdge.BOTTOM); 325 if (currentGridBandIsDark) { 326 g2.setPaint(this.gridBandPaint); 327 } else { 328 g2.setPaint(this.gridBandAlternatePaint); 329 } 330 band = new Rectangle2D.Double(Math.min(xx1, xx2), 331 yy + outlineStrokeWidth, Math.abs(xx2 - xx1), 332 dataArea.getMaxY() - yy - outlineStrokeWidth); 333 g2.fill(band); 334 currentGridBandIsDark = !currentGridBandIsDark; 335 } 336 } 337 338 /** 339 * Draws the grid bands for an axis that is aligned to the left or 340 * right of the data area (that is, a vertical axis). 341 * 342 * @param g2 the graphics target ({@code null} not permitted). 343 * @param plotArea the area within which the plot is drawn (not used here). 344 * @param dataArea the area for the data (to which the axes are aligned, 345 * {@code null} not permitted). 346 * @param firstGridBandIsDark True: the first grid band takes the 347 * color of {@code gridBandPaint}. 348 * False: the second grid band takes the 349 * color of {@code gridBandPaint}. 350 * @param ticks a list of ticks ({@code null} not permitted). 351 */ 352 protected void drawGridBandsVertical(Graphics2D g2, Rectangle2D plotArea, 353 Rectangle2D dataArea, boolean firstGridBandIsDark, 354 List ticks) { 355 356 boolean currentGridBandIsDark = firstGridBandIsDark; 357 double xx = dataArea.getX(); 358 double yy1, yy2; 359 360 //gets the outline stroke width of the plot 361 double outlineStrokeWidth = 1.0; 362 Stroke outlineStroke = getPlot().getOutlineStroke(); 363 if (outlineStroke != null && outlineStroke instanceof BasicStroke) { 364 outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth(); 365 } 366 367 Iterator iterator = ticks.iterator(); 368 ValueTick tick; 369 Rectangle2D band; 370 while (iterator.hasNext()) { 371 tick = (ValueTick) iterator.next(); 372 yy1 = valueToJava2D(tick.getValue() + 0.5d, dataArea, 373 RectangleEdge.LEFT); 374 yy2 = valueToJava2D(tick.getValue() - 0.5d, dataArea, 375 RectangleEdge.LEFT); 376 if (currentGridBandIsDark) { 377 g2.setPaint(this.gridBandPaint); 378 } else { 379 g2.setPaint(this.gridBandAlternatePaint); 380 } 381 band = new Rectangle2D.Double(xx + outlineStrokeWidth, 382 Math.min(yy1, yy2), dataArea.getMaxX() - xx 383 - outlineStrokeWidth, Math.abs(yy2 - yy1)); 384 g2.fill(band); 385 currentGridBandIsDark = !currentGridBandIsDark; 386 } 387 } 388 389 /** 390 * Rescales the axis to ensure that all data is visible. 391 */ 392 @Override 393 protected void autoAdjustRange() { 394 Plot plot = getPlot(); 395 if (plot == null) { 396 return; // no plot, no data 397 } 398 399 if (plot instanceof ValueAxisPlot) { 400 401 // ensure that all the symbols are displayed 402 double upper = this.symbols.size() - 1; 403 double lower = 0; 404 double range = upper - lower; 405 406 // ensure the autorange is at least <minRange> in size... 407 double minRange = getAutoRangeMinimumSize(); 408 if (range < minRange) { 409 upper = (upper + lower + minRange) / 2; 410 lower = (upper + lower - minRange) / 2; 411 } 412 413 // this ensure that the grid bands will be displayed correctly. 414 double upperMargin = 0.5; 415 double lowerMargin = 0.5; 416 417 if (getAutoRangeIncludesZero()) { 418 if (getAutoRangeStickyZero()) { 419 if (upper <= 0.0) { 420 upper = 0.0; 421 } else { 422 upper = upper + upperMargin; 423 } 424 if (lower >= 0.0) { 425 lower = 0.0; 426 } else { 427 lower = lower - lowerMargin; 428 } 429 } else { 430 upper = Math.max(0.0, upper + upperMargin); 431 lower = Math.min(0.0, lower - lowerMargin); 432 } 433 } else { 434 if (getAutoRangeStickyZero()) { 435 if (upper <= 0.0) { 436 upper = Math.min(0.0, upper + upperMargin); 437 } else { 438 upper = upper + upperMargin * range; 439 } 440 if (lower >= 0.0) { 441 lower = Math.max(0.0, lower - lowerMargin); 442 } else { 443 lower = lower - lowerMargin; 444 } 445 } else { 446 upper = upper + upperMargin; 447 lower = lower - lowerMargin; 448 } 449 } 450 setRange(new Range(lower, upper), false, false); 451 } 452 } 453 454 /** 455 * Calculates the positions of the tick labels for the axis, storing the 456 * results in the tick label list (ready for drawing). 457 * 458 * @param g2 the graphics device. 459 * @param state the axis state. 460 * @param dataArea the area in which the data should be drawn. 461 * @param edge the location of the axis. 462 * 463 * @return A list of ticks. 464 */ 465 @Override 466 public List refreshTicks(Graphics2D g2, AxisState state, 467 Rectangle2D dataArea, RectangleEdge edge) { 468 List ticks = null; 469 if (RectangleEdge.isTopOrBottom(edge)) { 470 ticks = refreshTicksHorizontal(g2, dataArea, edge); 471 } else if (RectangleEdge.isLeftOrRight(edge)) { 472 ticks = refreshTicksVertical(g2, dataArea, edge); 473 } 474 return ticks; 475 } 476 477 /** 478 * Calculates the positions of the tick labels for the axis, storing the 479 * results in the tick label list (ready for drawing). 480 * 481 * @param g2 the graphics device. 482 * @param dataArea the area in which the data should be drawn. 483 * @param edge the location of the axis. 484 * 485 * @return The ticks. 486 */ 487 @Override 488 protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, 489 RectangleEdge edge) { 490 491 List ticks = new java.util.ArrayList(); 492 493 Font tickLabelFont = getTickLabelFont(); 494 g2.setFont(tickLabelFont); 495 496 double size = getTickUnit().getSize(); 497 int count = calculateVisibleTickCount(); 498 double lowestTickValue = calculateLowestVisibleTickValue(); 499 500 double previousDrawnTickLabelPos = 0.0; 501 double previousDrawnTickLabelLength = 0.0; 502 503 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 504 for (int i = 0; i < count; i++) { 505 double currentTickValue = lowestTickValue + (i * size); 506 double xx = valueToJava2D(currentTickValue, dataArea, edge); 507 String tickLabel; 508 NumberFormat formatter = getNumberFormatOverride(); 509 if (formatter != null) { 510 tickLabel = formatter.format(currentTickValue); 511 } 512 else { 513 tickLabel = valueToString(currentTickValue); 514 } 515 516 // avoid to draw overlapping tick labels 517 Rectangle2D bounds = TextUtils.getTextBounds(tickLabel, g2, 518 g2.getFontMetrics()); 519 double tickLabelLength = isVerticalTickLabels() 520 ? bounds.getHeight() : bounds.getWidth(); 521 boolean tickLabelsOverlapping = false; 522 if (i > 0) { 523 double avgTickLabelLength = (previousDrawnTickLabelLength 524 + tickLabelLength) / 2.0; 525 if (Math.abs(xx - previousDrawnTickLabelPos) 526 < avgTickLabelLength) { 527 tickLabelsOverlapping = true; 528 } 529 } 530 if (tickLabelsOverlapping) { 531 tickLabel = ""; // don't draw this tick label 532 } 533 else { 534 // remember these values for next comparison 535 previousDrawnTickLabelPos = xx; 536 previousDrawnTickLabelLength = tickLabelLength; 537 } 538 539 TextAnchor anchor; 540 TextAnchor rotationAnchor; 541 double angle = 0.0; 542 if (isVerticalTickLabels()) { 543 anchor = TextAnchor.CENTER_RIGHT; 544 rotationAnchor = TextAnchor.CENTER_RIGHT; 545 if (edge == RectangleEdge.TOP) { 546 angle = Math.PI / 2.0; 547 } 548 else { 549 angle = -Math.PI / 2.0; 550 } 551 } 552 else { 553 if (edge == RectangleEdge.TOP) { 554 anchor = TextAnchor.BOTTOM_CENTER; 555 rotationAnchor = TextAnchor.BOTTOM_CENTER; 556 } 557 else { 558 anchor = TextAnchor.TOP_CENTER; 559 rotationAnchor = TextAnchor.TOP_CENTER; 560 } 561 } 562 Tick tick = new NumberTick(currentTickValue, 563 tickLabel, anchor, rotationAnchor, angle); 564 ticks.add(tick); 565 } 566 } 567 return ticks; 568 569 } 570 571 /** 572 * Calculates the positions of the tick labels for the axis, storing the 573 * results in the tick label list (ready for drawing). 574 * 575 * @param g2 the graphics device. 576 * @param dataArea the area in which the plot should be drawn. 577 * @param edge the location of the axis. 578 * 579 * @return The ticks. 580 */ 581 @Override 582 protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, 583 RectangleEdge edge) { 584 585 List ticks = new java.util.ArrayList(); 586 587 Font tickLabelFont = getTickLabelFont(); 588 g2.setFont(tickLabelFont); 589 590 double size = getTickUnit().getSize(); 591 int count = calculateVisibleTickCount(); 592 double lowestTickValue = calculateLowestVisibleTickValue(); 593 594 double previousDrawnTickLabelPos = 0.0; 595 double previousDrawnTickLabelLength = 0.0; 596 597 if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { 598 for (int i = 0; i < count; i++) { 599 double currentTickValue = lowestTickValue + (i * size); 600 double yy = valueToJava2D(currentTickValue, dataArea, edge); 601 String tickLabel; 602 NumberFormat formatter = getNumberFormatOverride(); 603 if (formatter != null) { 604 tickLabel = formatter.format(currentTickValue); 605 } 606 else { 607 tickLabel = valueToString(currentTickValue); 608 } 609 610 // avoid to draw overlapping tick labels 611 Rectangle2D bounds = TextUtils.getTextBounds(tickLabel, g2, 612 g2.getFontMetrics()); 613 double tickLabelLength = isVerticalTickLabels() 614 ? bounds.getWidth() : bounds.getHeight(); 615 boolean tickLabelsOverlapping = false; 616 if (i > 0) { 617 double avgTickLabelLength = (previousDrawnTickLabelLength 618 + tickLabelLength) / 2.0; 619 if (Math.abs(yy - previousDrawnTickLabelPos) 620 < avgTickLabelLength) { 621 tickLabelsOverlapping = true; 622 } 623 } 624 if (tickLabelsOverlapping) { 625 tickLabel = ""; // don't draw this tick label 626 } 627 else { 628 // remember these values for next comparison 629 previousDrawnTickLabelPos = yy; 630 previousDrawnTickLabelLength = tickLabelLength; 631 } 632 633 TextAnchor anchor; 634 TextAnchor rotationAnchor; 635 double angle = 0.0; 636 if (isVerticalTickLabels()) { 637 anchor = TextAnchor.BOTTOM_CENTER; 638 rotationAnchor = TextAnchor.BOTTOM_CENTER; 639 if (edge == RectangleEdge.LEFT) { 640 angle = -Math.PI / 2.0; 641 } 642 else { 643 angle = Math.PI / 2.0; 644 } 645 } 646 else { 647 if (edge == RectangleEdge.LEFT) { 648 anchor = TextAnchor.CENTER_RIGHT; 649 rotationAnchor = TextAnchor.CENTER_RIGHT; 650 } 651 else { 652 anchor = TextAnchor.CENTER_LEFT; 653 rotationAnchor = TextAnchor.CENTER_LEFT; 654 } 655 } 656 Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, 657 rotationAnchor, angle); 658 ticks.add(tick); 659 } 660 } 661 return ticks; 662 663 } 664 665 /** 666 * Converts a value to a string, using the list of symbols. 667 * 668 * @param value value to convert. 669 * 670 * @return The symbol. 671 */ 672 public String valueToString(double value) { 673 String strToReturn; 674 try { 675 strToReturn = (String) this.symbols.get((int) value); 676 } 677 catch (IndexOutOfBoundsException ex) { 678 strToReturn = ""; 679 } 680 return strToReturn; 681 } 682 683 /** 684 * Tests this axis for equality with an arbitrary object. 685 * 686 * @param obj the object ({@code null} permitted). 687 * 688 * @return A boolean. 689 */ 690 @Override 691 public boolean equals(Object obj) { 692 if (obj == this) { 693 return true; 694 } 695 if (!(obj instanceof SymbolAxis)) { 696 return false; 697 } 698 SymbolAxis that = (SymbolAxis) obj; 699 if (!this.symbols.equals(that.symbols)) { 700 return false; 701 } 702 if (this.gridBandsVisible != that.gridBandsVisible) { 703 return false; 704 } 705 if (!PaintUtils.equal(this.gridBandPaint, that.gridBandPaint)) { 706 return false; 707 } 708 if (!PaintUtils.equal(this.gridBandAlternatePaint, 709 that.gridBandAlternatePaint)) { 710 return false; 711 } 712 return super.equals(obj); 713 } 714 715 /** 716 * Provides serialization support. 717 * 718 * @param stream the output stream. 719 * 720 * @throws IOException if there is an I/O error. 721 */ 722 private void writeObject(ObjectOutputStream stream) throws IOException { 723 stream.defaultWriteObject(); 724 SerialUtils.writePaint(this.gridBandPaint, stream); 725 SerialUtils.writePaint(this.gridBandAlternatePaint, stream); 726 } 727 728 /** 729 * Provides serialization support. 730 * 731 * @param stream the input stream. 732 * 733 * @throws IOException if there is an I/O error. 734 * @throws ClassNotFoundException if there is a classpath problem. 735 */ 736 private void readObject(ObjectInputStream stream) 737 throws IOException, ClassNotFoundException { 738 stream.defaultReadObject(); 739 this.gridBandPaint = SerialUtils.readPaint(stream); 740 this.gridBandAlternatePaint = SerialUtils.readPaint(stream); 741 } 742 743}