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