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 * PaintScaleLegend.java 029 * --------------------- 030 * (C) Copyright 2007-present, by David Gilbert. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Peter Kolb - see patch 2686872; 034 * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); 035 * 036 */ 037 038package org.jfree.chart.title; 039 040import java.awt.BasicStroke; 041import java.awt.Color; 042import java.awt.Graphics2D; 043import java.awt.Paint; 044import java.awt.Stroke; 045import java.awt.geom.Rectangle2D; 046import java.io.IOException; 047import java.io.ObjectInputStream; 048import java.io.ObjectOutputStream; 049import java.util.Objects; 050import org.jfree.chart.HashUtils; 051 052import org.jfree.chart.axis.AxisLocation; 053import org.jfree.chart.axis.AxisSpace; 054import org.jfree.chart.axis.ValueAxis; 055import org.jfree.chart.block.LengthConstraintType; 056import org.jfree.chart.block.RectangleConstraint; 057import org.jfree.chart.event.AxisChangeEvent; 058import org.jfree.chart.event.AxisChangeListener; 059import org.jfree.chart.event.TitleChangeEvent; 060import org.jfree.chart.plot.Plot; 061import org.jfree.chart.plot.PlotOrientation; 062import org.jfree.chart.renderer.PaintScale; 063import org.jfree.chart.ui.RectangleEdge; 064import org.jfree.chart.ui.Size2D; 065import org.jfree.chart.util.PaintUtils; 066import org.jfree.chart.util.Args; 067import org.jfree.chart.util.PublicCloneable; 068import org.jfree.chart.util.SerialUtils; 069import org.jfree.data.Range; 070 071/** 072 * A legend that shows a range of values and their associated colors, driven 073 * by an underlying {@link PaintScale} implementation. 074 */ 075public class PaintScaleLegend extends Title implements AxisChangeListener, 076 PublicCloneable { 077 078 /** For serialization. */ 079 static final long serialVersionUID = -1365146490993227503L; 080 081 /** The paint scale (never {@code null}). */ 082 private PaintScale scale; 083 084 /** The value axis (never {@code null}). */ 085 private ValueAxis axis; 086 087 /** 088 * The axis location (handles both orientations, never 089 * {@code null}). 090 */ 091 private AxisLocation axisLocation; 092 093 /** The offset between the axis and the paint strip (in Java2D units). */ 094 private double axisOffset; 095 096 /** The thickness of the paint strip (in Java2D units). */ 097 private double stripWidth; 098 099 /** 100 * A flag that controls whether or not an outline is drawn around the 101 * paint strip. 102 */ 103 private boolean stripOutlineVisible; 104 105 /** The paint used to draw an outline around the paint strip. */ 106 private transient Paint stripOutlinePaint; 107 108 /** The stroke used to draw an outline around the paint strip. */ 109 private transient Stroke stripOutlineStroke; 110 111 /** The background paint (never {@code null}). */ 112 private transient Paint backgroundPaint; 113 114 /** 115 * The number of subdivisions for the scale when rendering. 116 */ 117 private int subdivisions; 118 119 /** 120 * Creates a new instance. 121 * 122 * @param scale the scale ({@code null} not permitted). 123 * @param axis the axis ({@code null} not permitted). 124 */ 125 public PaintScaleLegend(PaintScale scale, ValueAxis axis) { 126 Args.nullNotPermitted(axis, "axis"); 127 this.scale = scale; 128 this.axis = axis; 129 this.axis.addChangeListener(this); 130 this.axisLocation = AxisLocation.BOTTOM_OR_LEFT; 131 this.axisOffset = 0.0; 132 this.axis.setRange(scale.getLowerBound(), scale.getUpperBound()); 133 this.stripWidth = 15.0; 134 this.stripOutlineVisible = true; 135 this.stripOutlinePaint = Color.GRAY; 136 this.stripOutlineStroke = new BasicStroke(0.5f); 137 this.backgroundPaint = Color.WHITE; 138 this.subdivisions = 100; 139 } 140 141 /** 142 * Returns the scale used to convert values to colors. 143 * 144 * @return The scale (never {@code null}). 145 * 146 * @see #setScale(PaintScale) 147 */ 148 public PaintScale getScale() { 149 return this.scale; 150 } 151 152 /** 153 * Sets the scale and sends a {@link TitleChangeEvent} to all registered 154 * listeners. 155 * 156 * @param scale the scale ({@code null} not permitted). 157 * 158 * @see #getScale() 159 */ 160 public void setScale(PaintScale scale) { 161 Args.nullNotPermitted(scale, "scale"); 162 this.scale = scale; 163 notifyListeners(new TitleChangeEvent(this)); 164 } 165 166 /** 167 * Returns the axis for the paint scale. 168 * 169 * @return The axis (never {@code null}). 170 * 171 * @see #setAxis(ValueAxis) 172 */ 173 public ValueAxis getAxis() { 174 return this.axis; 175 } 176 177 /** 178 * Sets the axis for the paint scale and sends a {@link TitleChangeEvent} 179 * to all registered listeners. 180 * 181 * @param axis the axis ({@code null} not permitted). 182 * 183 * @see #getAxis() 184 */ 185 public void setAxis(ValueAxis axis) { 186 Args.nullNotPermitted(axis, "axis"); 187 this.axis.removeChangeListener(this); 188 this.axis = axis; 189 this.axis.addChangeListener(this); 190 notifyListeners(new TitleChangeEvent(this)); 191 } 192 193 /** 194 * Returns the axis location. 195 * 196 * @return The axis location (never {@code null}). 197 * 198 * @see #setAxisLocation(AxisLocation) 199 */ 200 public AxisLocation getAxisLocation() { 201 return this.axisLocation; 202 } 203 204 /** 205 * Sets the axis location and sends a {@link TitleChangeEvent} to all 206 * registered listeners. 207 * 208 * @param location the location ({@code null} not permitted). 209 * 210 * @see #getAxisLocation() 211 */ 212 public void setAxisLocation(AxisLocation location) { 213 Args.nullNotPermitted(location, "location"); 214 this.axisLocation = location; 215 notifyListeners(new TitleChangeEvent(this)); 216 } 217 218 /** 219 * Returns the offset between the axis and the paint strip. 220 * 221 * @return The offset between the axis and the paint strip. 222 * 223 * @see #setAxisOffset(double) 224 */ 225 public double getAxisOffset() { 226 return this.axisOffset; 227 } 228 229 /** 230 * Sets the offset between the axis and the paint strip and sends a 231 * {@link TitleChangeEvent} to all registered listeners. 232 * 233 * @param offset the offset. 234 */ 235 public void setAxisOffset(double offset) { 236 this.axisOffset = offset; 237 notifyListeners(new TitleChangeEvent(this)); 238 } 239 240 /** 241 * Returns the width of the paint strip, in Java2D units. 242 * 243 * @return The width of the paint strip. 244 * 245 * @see #setStripWidth(double) 246 */ 247 public double getStripWidth() { 248 return this.stripWidth; 249 } 250 251 /** 252 * Sets the width of the paint strip and sends a {@link TitleChangeEvent} 253 * to all registered listeners. 254 * 255 * @param width the width. 256 * 257 * @see #getStripWidth() 258 */ 259 public void setStripWidth(double width) { 260 this.stripWidth = width; 261 notifyListeners(new TitleChangeEvent(this)); 262 } 263 264 /** 265 * Returns the flag that controls whether or not an outline is drawn 266 * around the paint strip. 267 * 268 * @return A boolean. 269 * 270 * @see #setStripOutlineVisible(boolean) 271 */ 272 public boolean isStripOutlineVisible() { 273 return this.stripOutlineVisible; 274 } 275 276 /** 277 * Sets the flag that controls whether or not an outline is drawn around 278 * the paint strip, and sends a {@link TitleChangeEvent} to all registered 279 * listeners. 280 * 281 * @param visible the flag. 282 * 283 * @see #isStripOutlineVisible() 284 */ 285 public void setStripOutlineVisible(boolean visible) { 286 this.stripOutlineVisible = visible; 287 notifyListeners(new TitleChangeEvent(this)); 288 } 289 290 /** 291 * Returns the paint used to draw the outline of the paint strip. 292 * 293 * @return The paint (never {@code null}). 294 * 295 * @see #setStripOutlinePaint(Paint) 296 */ 297 public Paint getStripOutlinePaint() { 298 return this.stripOutlinePaint; 299 } 300 301 /** 302 * Sets the paint used to draw the outline of the paint strip, and sends 303 * a {@link TitleChangeEvent} to all registered listeners. 304 * 305 * @param paint the paint ({@code null} not permitted). 306 * 307 * @see #getStripOutlinePaint() 308 */ 309 public void setStripOutlinePaint(Paint paint) { 310 Args.nullNotPermitted(paint, "paint"); 311 this.stripOutlinePaint = paint; 312 notifyListeners(new TitleChangeEvent(this)); 313 } 314 315 /** 316 * Returns the stroke used to draw the outline around the paint strip. 317 * 318 * @return The stroke (never {@code null}). 319 * 320 * @see #setStripOutlineStroke(Stroke) 321 */ 322 public Stroke getStripOutlineStroke() { 323 return this.stripOutlineStroke; 324 } 325 326 /** 327 * Sets the stroke used to draw the outline around the paint strip and 328 * sends a {@link TitleChangeEvent} to all registered listeners. 329 * 330 * @param stroke the stroke ({@code null} not permitted). 331 * 332 * @see #getStripOutlineStroke() 333 */ 334 public void setStripOutlineStroke(Stroke stroke) { 335 Args.nullNotPermitted(stroke, "stroke"); 336 this.stripOutlineStroke = stroke; 337 notifyListeners(new TitleChangeEvent(this)); 338 } 339 340 /** 341 * Returns the background paint. 342 * 343 * @return The background paint. 344 */ 345 public Paint getBackgroundPaint() { 346 return this.backgroundPaint; 347 } 348 349 /** 350 * Sets the background paint and sends a {@link TitleChangeEvent} to all 351 * registered listeners. 352 * 353 * @param paint the paint ({@code null} permitted). 354 */ 355 public void setBackgroundPaint(Paint paint) { 356 this.backgroundPaint = paint; 357 notifyListeners(new TitleChangeEvent(this)); 358 } 359 360 /** 361 * Returns the number of subdivisions used to draw the scale. 362 * 363 * @return The subdivision count. 364 */ 365 public int getSubdivisionCount() { 366 return this.subdivisions; 367 } 368 369 /** 370 * Sets the subdivision count and sends a {@link TitleChangeEvent} to 371 * all registered listeners. 372 * 373 * @param count the count. 374 */ 375 public void setSubdivisionCount(int count) { 376 if (count <= 0) { 377 throw new IllegalArgumentException("Requires 'count' > 0."); 378 } 379 this.subdivisions = count; 380 notifyListeners(new TitleChangeEvent(this)); 381 } 382 383 /** 384 * Receives notification of an axis change event and responds by firing 385 * a title change event. 386 * 387 * @param event the event. 388 */ 389 @Override 390 public void axisChanged(AxisChangeEvent event) { 391 if (this.axis == event.getAxis()) { 392 notifyListeners(new TitleChangeEvent(this)); 393 } 394 } 395 396 /** 397 * Arranges the contents of the block, within the given constraints, and 398 * returns the block size. 399 * 400 * @param g2 the graphics device. 401 * @param constraint the constraint ({@code null} not permitted). 402 * 403 * @return The block size (in Java2D units, never {@code null}). 404 */ 405 @Override 406 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 407 RectangleConstraint cc = toContentConstraint(constraint); 408 LengthConstraintType w = cc.getWidthConstraintType(); 409 LengthConstraintType h = cc.getHeightConstraintType(); 410 Size2D contentSize = null; 411 if (w == LengthConstraintType.NONE) { 412 if (h == LengthConstraintType.NONE) { 413 contentSize = new Size2D(getWidth(), getHeight()); 414 } 415 else if (h == LengthConstraintType.RANGE) { 416 throw new RuntimeException("Not yet implemented."); 417 } 418 else if (h == LengthConstraintType.FIXED) { 419 throw new RuntimeException("Not yet implemented."); 420 } 421 } 422 else if (w == LengthConstraintType.RANGE) { 423 if (h == LengthConstraintType.NONE) { 424 throw new RuntimeException("Not yet implemented."); 425 } 426 else if (h == LengthConstraintType.RANGE) { 427 contentSize = arrangeRR(g2, cc.getWidthRange(), 428 cc.getHeightRange()); 429 } 430 else if (h == LengthConstraintType.FIXED) { 431 throw new RuntimeException("Not yet implemented."); 432 } 433 } 434 else if (w == LengthConstraintType.FIXED) { 435 if (h == LengthConstraintType.NONE) { 436 throw new RuntimeException("Not yet implemented."); 437 } 438 else if (h == LengthConstraintType.RANGE) { 439 throw new RuntimeException("Not yet implemented."); 440 } 441 else if (h == LengthConstraintType.FIXED) { 442 throw new RuntimeException("Not yet implemented."); 443 } 444 } 445 assert contentSize != null; // suppress compiler warning 446 return new Size2D(calculateTotalWidth(contentSize.getWidth()), 447 calculateTotalHeight(contentSize.getHeight())); 448 } 449 450 /** 451 * Returns the content size for the title. This will reflect the fact that 452 * a text title positioned on the left or right of a chart will be rotated 453 * 90 degrees. 454 * 455 * @param g2 the graphics device. 456 * @param widthRange the width range. 457 * @param heightRange the height range. 458 * 459 * @return The content size. 460 */ 461 protected Size2D arrangeRR(Graphics2D g2, Range widthRange, 462 Range heightRange) { 463 464 RectangleEdge position = getPosition(); 465 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 466 467 468 float maxWidth = (float) widthRange.getUpperBound(); 469 470 // determine the space required for the axis 471 AxisSpace space = this.axis.reserveSpace(g2, null, 472 new Rectangle2D.Double(0, 0, maxWidth, 100), 473 RectangleEdge.BOTTOM, null); 474 475 return new Size2D(maxWidth, this.stripWidth + this.axisOffset 476 + space.getTop() + space.getBottom()); 477 } 478 else if (position == RectangleEdge.LEFT || position 479 == RectangleEdge.RIGHT) { 480 float maxHeight = (float) heightRange.getUpperBound(); 481 AxisSpace space = this.axis.reserveSpace(g2, null, 482 new Rectangle2D.Double(0, 0, 100, maxHeight), 483 RectangleEdge.RIGHT, null); 484 return new Size2D(this.stripWidth + this.axisOffset 485 + space.getLeft() + space.getRight(), maxHeight); 486 } 487 else { 488 throw new RuntimeException("Unrecognised position."); 489 } 490 } 491 492 /** 493 * Draws the legend within the specified area. 494 * 495 * @param g2 the graphics target ({@code null} not permitted). 496 * @param area the drawing area ({@code null} not permitted). 497 */ 498 @Override 499 public void draw(Graphics2D g2, Rectangle2D area) { 500 draw(g2, area, null); 501 } 502 503 /** 504 * Draws the legend within the specified area. 505 * 506 * @param g2 the graphics target ({@code null} not permitted). 507 * @param area the drawing area ({@code null} not permitted). 508 * @param params drawing parameters (ignored here). 509 * 510 * @return {@code null}. 511 */ 512 @Override 513 public Object draw(Graphics2D g2, Rectangle2D area, Object params) { 514 Rectangle2D target = (Rectangle2D) area.clone(); 515 target = trimMargin(target); 516 if (this.backgroundPaint != null) { 517 g2.setPaint(this.backgroundPaint); 518 g2.fill(target); 519 } 520 getFrame().draw(g2, target); 521 getFrame().getInsets().trim(target); 522 target = trimPadding(target); 523 double base = this.axis.getLowerBound(); 524 double increment = this.axis.getRange().getLength() / this.subdivisions; 525 Rectangle2D r = new Rectangle2D.Double(); 526 527 if (RectangleEdge.isTopOrBottom(getPosition())) { 528 RectangleEdge axisEdge = Plot.resolveRangeAxisLocation( 529 this.axisLocation, PlotOrientation.HORIZONTAL); 530 if (axisEdge == RectangleEdge.TOP) { 531 for (int i = 0; i < this.subdivisions; i++) { 532 double v = base + (i * increment); 533 Paint p = this.scale.getPaint(v); 534 double vv0 = this.axis.valueToJava2D(v, target, 535 RectangleEdge.TOP); 536 double vv1 = this.axis.valueToJava2D(v + increment, target, 537 RectangleEdge.TOP); 538 double ww = Math.abs(vv1 - vv0) + 1.0; 539 r.setRect(Math.min(vv0, vv1), target.getMaxY() 540 - this.stripWidth, ww, this.stripWidth); 541 g2.setPaint(p); 542 g2.fill(r); 543 } 544 if (isStripOutlineVisible()) { 545 g2.setPaint(this.stripOutlinePaint); 546 g2.setStroke(this.stripOutlineStroke); 547 g2.draw(new Rectangle2D.Double(target.getMinX(), 548 target.getMaxY() - this.stripWidth, 549 target.getWidth(), this.stripWidth)); 550 } 551 this.axis.draw(g2, target.getMaxY() - this.stripWidth 552 - this.axisOffset, target, target, RectangleEdge.TOP, 553 null); 554 } 555 else if (axisEdge == RectangleEdge.BOTTOM) { 556 for (int i = 0; i < this.subdivisions; i++) { 557 double v = base + (i * increment); 558 Paint p = this.scale.getPaint(v); 559 double vv0 = this.axis.valueToJava2D(v, target, 560 RectangleEdge.BOTTOM); 561 double vv1 = this.axis.valueToJava2D(v + increment, target, 562 RectangleEdge.BOTTOM); 563 double ww = Math.abs(vv1 - vv0) + 1.0; 564 r.setRect(Math.min(vv0, vv1), target.getMinY(), ww, 565 this.stripWidth); 566 g2.setPaint(p); 567 g2.fill(r); 568 } 569 if (isStripOutlineVisible()) { 570 g2.setPaint(this.stripOutlinePaint); 571 g2.setStroke(this.stripOutlineStroke); 572 g2.draw(new Rectangle2D.Double(target.getMinX(), 573 target.getMinY(), target.getWidth(), 574 this.stripWidth)); 575 } 576 this.axis.draw(g2, target.getMinY() + this.stripWidth 577 + this.axisOffset, target, target, 578 RectangleEdge.BOTTOM, null); 579 } 580 } 581 else { 582 RectangleEdge axisEdge = Plot.resolveRangeAxisLocation( 583 this.axisLocation, PlotOrientation.VERTICAL); 584 if (axisEdge == RectangleEdge.LEFT) { 585 for (int i = 0; i < this.subdivisions; i++) { 586 double v = base + (i * increment); 587 Paint p = this.scale.getPaint(v); 588 double vv0 = this.axis.valueToJava2D(v, target, 589 RectangleEdge.LEFT); 590 double vv1 = this.axis.valueToJava2D(v + increment, target, 591 RectangleEdge.LEFT); 592 double hh = Math.abs(vv1 - vv0) + 1.0; 593 r.setRect(target.getMaxX() - this.stripWidth, 594 Math.min(vv0, vv1), this.stripWidth, hh); 595 g2.setPaint(p); 596 g2.fill(r); 597 } 598 if (isStripOutlineVisible()) { 599 g2.setPaint(this.stripOutlinePaint); 600 g2.setStroke(this.stripOutlineStroke); 601 g2.draw(new Rectangle2D.Double(target.getMaxX() 602 - this.stripWidth, target.getMinY(), 603 this.stripWidth, target.getHeight())); 604 } 605 this.axis.draw(g2, target.getMaxX() - this.stripWidth 606 - this.axisOffset, target, target, RectangleEdge.LEFT, 607 null); 608 } 609 else if (axisEdge == RectangleEdge.RIGHT) { 610 for (int i = 0; i < this.subdivisions; i++) { 611 double v = base + (i * increment); 612 Paint p = this.scale.getPaint(v); 613 double vv0 = this.axis.valueToJava2D(v, target, 614 RectangleEdge.LEFT); 615 double vv1 = this.axis.valueToJava2D(v + increment, target, 616 RectangleEdge.LEFT); 617 double hh = Math.abs(vv1 - vv0) + 1.0; 618 r.setRect(target.getMinX(), Math.min(vv0, vv1), 619 this.stripWidth, hh); 620 g2.setPaint(p); 621 g2.fill(r); 622 } 623 if (isStripOutlineVisible()) { 624 g2.setPaint(this.stripOutlinePaint); 625 g2.setStroke(this.stripOutlineStroke); 626 g2.draw(new Rectangle2D.Double(target.getMinX(), 627 target.getMinY(), this.stripWidth, 628 target.getHeight())); 629 } 630 this.axis.draw(g2, target.getMinX() + this.stripWidth 631 + this.axisOffset, target, target, RectangleEdge.RIGHT, 632 null); 633 } 634 } 635 return null; 636 } 637 638 /** 639 * Tests this legend for equality with an arbitrary object. 640 * 641 * @param obj the object ({@code null} permitted). 642 * 643 * @return A boolean. 644 */ 645 @Override 646 public boolean equals(Object obj) { 647 if (obj == this) { 648 return true; 649 } 650 if (!(obj instanceof PaintScaleLegend)) { 651 return false; 652 } 653 PaintScaleLegend that = (PaintScaleLegend) obj; 654 if (!Objects.equals(this.scale, that.scale)) { 655 return false; 656 } 657 if (!Objects.equals(this.axis, that.axis)) { 658 return false; 659 } 660 if (!Objects.equals(this.axisLocation, that.axisLocation)) { 661 return false; 662 } 663 if (Double.doubleToLongBits(this.axisOffset) != 664 Double.doubleToLongBits(that.axisOffset)) { 665 return false; 666 } 667 if (Double.doubleToLongBits(this.stripWidth) != 668 Double.doubleToLongBits(that.stripWidth)) { 669 return false; 670 } 671 if (this.stripOutlineVisible != that.stripOutlineVisible) { 672 return false; 673 } 674 if (!PaintUtils.equal(this.stripOutlinePaint, 675 that.stripOutlinePaint)) { 676 return false; 677 } 678 if (!Objects.equals(this.stripOutlineStroke, that.stripOutlineStroke)) { 679 return false; 680 } 681 if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { 682 return false; 683 } 684 if (this.subdivisions != that.subdivisions) { 685 return false; 686 } 687 if (!that.canEqual(this)) { 688 return false; 689 } 690 return super.equals(obj); 691 } 692 693 /** 694 * Ensures symmetry between super/subclass implementations of equals. For 695 * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. 696 * 697 * @param other Object 698 * 699 * @return true ONLY if the parameter is THIS class type 700 */ 701 @Override 702 public boolean canEqual(Object other) { 703 // fix the "equals not symmetric" problem 704 return (other instanceof PaintScaleLegend); 705 } 706 707 @Override 708 public int hashCode() { 709 int hash = super.hashCode(); // equals calls superclass, hashCode must also 710 hash = 53 * hash + Objects.hashCode(this.scale); 711 hash = 53 * hash + Objects.hashCode(this.axis); 712 hash = 53 * hash + Objects.hashCode(this.axisLocation); 713 hash = 53 * hash + (int) (Double.doubleToLongBits(this.axisOffset) ^ 714 (Double.doubleToLongBits(this.axisOffset) >>> 32)); 715 hash = 53 * hash + (int) (Double.doubleToLongBits(this.stripWidth) ^ 716 (Double.doubleToLongBits(this.stripWidth) >>> 32)); 717 hash = 53 * hash + (this.stripOutlineVisible ? 1 : 0); 718 hash = 53 * hash + HashUtils.hashCodeForPaint(this.stripOutlinePaint); 719 hash = 53 * hash + Objects.hashCode(this.stripOutlineStroke); 720 hash = 53 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); 721 hash = 53 * hash + this.subdivisions; 722 return hash; 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.backgroundPaint, stream); 735 SerialUtils.writePaint(this.stripOutlinePaint, stream); 736 SerialUtils.writeStroke(this.stripOutlineStroke, stream); 737 } 738 739 /** 740 * Provides serialization support. 741 * 742 * @param stream the input stream. 743 * 744 * @throws IOException if there is an I/O error. 745 * @throws ClassNotFoundException if there is a classpath problem. 746 */ 747 private void readObject(ObjectInputStream stream) 748 throws IOException, ClassNotFoundException { 749 stream.defaultReadObject(); 750 this.backgroundPaint = SerialUtils.readPaint(stream); 751 this.stripOutlinePaint = SerialUtils.readPaint(stream); 752 this.stripOutlineStroke = SerialUtils.readStroke(stream); 753 } 754 755}