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