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