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