Source for org.jfree.chart.title.PaintScaleLegend

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------------------
  28:  * PaintScaleLegend.java
  29:  * ---------------------
  30:  * (C) Copyright 2007, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 22-Jan-2007 : Version 1 (DG);
  38:  * 
  39:  */
  40: 
  41: package org.jfree.chart.title;
  42: 
  43: import java.awt.BasicStroke;
  44: import java.awt.Color;
  45: import java.awt.Graphics2D;
  46: import java.awt.Paint;
  47: import java.awt.Stroke;
  48: import java.awt.geom.Rectangle2D;
  49: import java.io.IOException;
  50: import java.io.ObjectInputStream;
  51: import java.io.ObjectOutputStream;
  52: 
  53: import org.jfree.chart.axis.AxisLocation;
  54: import org.jfree.chart.axis.AxisSpace;
  55: import org.jfree.chart.axis.ValueAxis;
  56: import org.jfree.chart.block.LengthConstraintType;
  57: import org.jfree.chart.block.RectangleConstraint;
  58: import org.jfree.chart.event.TitleChangeEvent;
  59: import org.jfree.chart.plot.Plot;
  60: import org.jfree.chart.plot.PlotOrientation;
  61: import org.jfree.chart.renderer.PaintScale;
  62: import org.jfree.data.Range;
  63: import org.jfree.io.SerialUtilities;
  64: import org.jfree.ui.RectangleEdge;
  65: import org.jfree.ui.Size2D;
  66: import org.jfree.util.PaintUtilities;
  67: import org.jfree.util.PublicCloneable;
  68: 
  69: /**
  70:  * A legend that shows a range of values and their associated colors, driven
  71:  * by an underlying {@link PaintScale} implementation.
  72:  * 
  73:  * @since 1.0.4
  74:  */
  75: public class PaintScaleLegend extends Title implements PublicCloneable {
  76: 
  77:     /** For serialization. */
  78:     static final long serialVersionUID = -1365146490993227503L;
  79:     
  80:     /** The paint scale (never <code>null</code>). */
  81:     private PaintScale scale;
  82:     
  83:     /** The value axis (never <code>null</code>). */
  84:     private ValueAxis axis;
  85:     
  86:     /** 
  87:      * The axis location (handles both orientations, never 
  88:      * <code>null</code>). 
  89:      */
  90:     private AxisLocation axisLocation;
  91: 
  92:     /** The offset between the axis and the paint strip (in Java2D units). */
  93:     private double axisOffset;
  94:     
  95:     /** The thickness of the paint strip (in Java2D units). */
  96:     private double stripWidth;
  97:    
  98:     /** 
  99:      * A flag that controls whether or not an outline is drawn around the
 100:      * paint strip.
 101:      */
 102:     private boolean stripOutlineVisible;
 103:     
 104:     /** The paint used to draw an outline around the paint strip. */
 105:     private transient Paint stripOutlinePaint;
 106:     
 107:     /** The stroke used to draw an outline around the paint strip. */
 108:     private transient Stroke stripOutlineStroke;
 109:     
 110:     /** The background paint (never <code>null</code>). */
 111:     private transient Paint backgroundPaint;
 112:     
 113:     /**
 114:      * Creates a new instance.
 115:      * 
 116:      * @param scale  the scale (<code>null</code> not permitted).
 117:      * @param axis  the axis (<code>null</code> not permitted).
 118:      */
 119:     public PaintScaleLegend(PaintScale scale, ValueAxis axis) {
 120:         if (axis == null) {
 121:             throw new IllegalArgumentException("Null 'axis' argument.");
 122:         }
 123:         this.scale = scale;
 124:         this.axis = axis;
 125:         this.axisLocation = AxisLocation.BOTTOM_OR_LEFT;
 126:         this.axisOffset = 0.0;
 127:         this.stripWidth = 15.0;
 128:         this.stripOutlineVisible = false;
 129:         this.stripOutlinePaint = Color.gray;
 130:         this.stripOutlineStroke = new BasicStroke(0.5f);
 131:         this.backgroundPaint = Color.white;
 132:     }
 133:     
 134:     /**
 135:      * Returns the scale used to convert values to colors.
 136:      * 
 137:      * @return The scale (never <code>null</code>).
 138:      * 
 139:      * @see #setScale(PaintScale)
 140:      */
 141:     public PaintScale getScale() {
 142:         return this.scale;    
 143:     }
 144:     
 145:     /**
 146:      * Sets the scale and sends a {@link TitleChangeEvent} to all registered
 147:      * listeners.
 148:      * 
 149:      * @param scale  the scale (<code>null</code> not permitted).
 150:      * 
 151:      * @see #getScale()
 152:      */
 153:     public void setScale(PaintScale scale) {
 154:         if (scale == null) {
 155:             throw new IllegalArgumentException("Null 'scale' argument.");
 156:         }
 157:         this.scale = scale;
 158:         notifyListeners(new TitleChangeEvent(this));
 159:     }
 160:     
 161:     /**
 162:      * Returns the axis for the paint scale.
 163:      * 
 164:      * @return The axis (never <code>null</code>).
 165:      * 
 166:      * @see #setAxis(ValueAxis)
 167:      */
 168:     public ValueAxis getAxis() {
 169:         return this.axis;
 170:     }
 171:     
 172:     /**
 173:      * Sets the axis for the paint scale and sends a {@link TitleChangeEvent}
 174:      * to all registered listeners.
 175:      * 
 176:      * @param axis  the axis (<code>null</code> not permitted).
 177:      * 
 178:      * @see #getAxis()
 179:      */
 180:     public void setAxis(ValueAxis axis) {
 181:         if (axis == null) {
 182:             throw new IllegalArgumentException("Null 'axis' argument.");
 183:         }
 184:         this.axis = axis;
 185:         notifyListeners(new TitleChangeEvent(this));
 186:     }
 187:     
 188:     /**
 189:      * Returns the axis location.
 190:      * 
 191:      * @return The axis location (never <code>null</code>).
 192:      * 
 193:      * @see #setAxisLocation(AxisLocation)
 194:      */
 195:     public AxisLocation getAxisLocation() {
 196:         return this.axisLocation;
 197:     }
 198:     
 199:     /**
 200:      * Sets the axis location and sends a {@link TitleChangeEvent} to all 
 201:      * registered listeners.
 202:      * 
 203:      * @param location  the location (<code>null</code> not permitted).
 204:      * 
 205:      * @see #getAxisLocation()
 206:      */
 207:     public void setAxisLocation(AxisLocation location) {
 208:         if (location == null) {
 209:             throw new IllegalArgumentException("Null 'location' argument.");
 210:         }
 211:         this.axisLocation = location;
 212:         notifyListeners(new TitleChangeEvent(this));
 213:     }
 214:     
 215:     /**
 216:      * Returns the offset between the axis and the paint strip.
 217:      * 
 218:      * @return The offset between the axis and the paint strip.
 219:      * 
 220:      * @see #setAxisOffset(double)
 221:      */
 222:     public double getAxisOffset() {
 223:         return this.axisOffset;
 224:     }
 225:     
 226:     /**
 227:      * Sets the offset between the axis and the paint strip and sends a 
 228:      * {@link TitleChangeEvent} to all registered listeners.
 229:      * 
 230:      * @param offset  the offset.
 231:      */
 232:     public void setAxisOffset(double offset) {
 233:         this.axisOffset = offset;
 234:         notifyListeners(new TitleChangeEvent(this));
 235:     }
 236:     
 237:     /**
 238:      * Returns the width of the paint strip, in Java2D units.
 239:      * 
 240:      * @return The width of the paint strip.
 241:      * 
 242:      * @see #setStripWidth(double)
 243:      */
 244:     public double getStripWidth() {
 245:         return this.stripWidth;
 246:     }
 247:     
 248:     /**
 249:      * Sets the width of the paint strip and sends a {@link TitleChangeEvent}
 250:      * to all registered listeners.
 251:      * 
 252:      * @param width  the width.
 253:      * 
 254:      * @see #getStripWidth()
 255:      */
 256:     public void setStripWidth(double width) {
 257:         this.stripWidth = width;
 258:         notifyListeners(new TitleChangeEvent(this));
 259:     }
 260:     
 261:     /**
 262:      * Returns the flag that controls whether or not an outline is drawn 
 263:      * around the paint strip.
 264:      * 
 265:      * @return A boolean.
 266:      * 
 267:      * @see #setStripOutlineVisible(boolean)
 268:      */
 269:     public boolean isStripOutlineVisible() {
 270:         return this.stripOutlineVisible;
 271:     }
 272:     
 273:     /**
 274:      * Sets the flag that controls whether or not an outline is drawn around
 275:      * the paint strip, and sends a {@link TitleChangeEvent} to all registered
 276:      * listeners.
 277:      * 
 278:      * @param visible  the flag.
 279:      * 
 280:      * @see #isStripOutlineVisible()
 281:      */
 282:     public void setStripOutlineVisible(boolean visible) {
 283:         this.stripOutlineVisible = visible;
 284:         notifyListeners(new TitleChangeEvent(this));
 285:     }
 286:     
 287:     /**
 288:      * Returns the paint used to draw the outline of the paint strip.
 289:      * 
 290:      * @return The paint (never <code>null</code>).
 291:      * 
 292:      * @see #setStripOutlinePaint(Paint)
 293:      */
 294:     public Paint getStripOutlinePaint() {
 295:         return this.stripOutlinePaint;
 296:     }
 297:     
 298:     /**
 299:      * Sets the paint used to draw the outline of the paint strip, and sends
 300:      * a {@link TitleChangeEvent} to all registered listeners.
 301:      * 
 302:      * @param paint  the paint (<code>null</code> not permitted).
 303:      * 
 304:      * @see #getStripOutlinePaint()
 305:      */
 306:     public void setStripOutlinePaint(Paint paint) {
 307:         if (paint == null) {
 308:             throw new IllegalArgumentException("Null 'paint' argument.");
 309:         }
 310:         this.stripOutlinePaint = paint;
 311:         notifyListeners(new TitleChangeEvent(this));
 312:     }
 313:     
 314:     /**
 315:      * Returns the stroke used to draw the outline around the paint strip.
 316:      * 
 317:      * @return The stroke (never <code>null</code>).
 318:      * 
 319:      * @see #setStripOutlineStroke(Stroke)
 320:      */
 321:     public Stroke getStripOutlineStroke() {
 322:         return this.stripOutlineStroke;
 323:     }
 324:     
 325:     /**
 326:      * Sets the stroke used to draw the outline around the paint strip and 
 327:      * sends a {@link TitleChangeEvent} to all registered listeners.
 328:      * 
 329:      * @param stroke  the stroke (<code>null</code> not permitted).
 330:      * 
 331:      * @see #getStripOutlineStroke()
 332:      */
 333:     public void setStripOutlineStroke(Stroke stroke) {
 334:         if (stroke == null) {
 335:             throw new IllegalArgumentException("Null 'stroke' argument.");
 336:         }
 337:         this.stripOutlineStroke = stroke;
 338:         notifyListeners(new TitleChangeEvent(this));
 339:     }
 340:     
 341:     /**
 342:      * Returns the background paint.
 343:      * 
 344:      * @return The background paint.
 345:      */
 346:     public Paint getBackgroundPaint() {
 347:         return this.backgroundPaint;
 348:     }
 349:     
 350:     /**
 351:      * Sets the background paint and sends a {@link TitleChangeEvent} to all
 352:      * registered listeners.
 353:      * 
 354:      * @param paint  the paint (<code>null</code> permitted).
 355:      */
 356:     public void setBackgroundPaint(Paint paint) {
 357:         this.backgroundPaint = paint;
 358:         notifyListeners(new TitleChangeEvent(this));
 359:     }
 360:     
 361:     /**
 362:      * Arranges the contents of the block, within the given constraints, and 
 363:      * returns the block size.
 364:      * 
 365:      * @param g2  the graphics device.
 366:      * @param constraint  the constraint (<code>null</code> not permitted).
 367:      * 
 368:      * @return The block size (in Java2D units, never <code>null</code>).
 369:      */
 370:     public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
 371:         RectangleConstraint cc = toContentConstraint(constraint);
 372:         LengthConstraintType w = cc.getWidthConstraintType();
 373:         LengthConstraintType h = cc.getHeightConstraintType();
 374:         Size2D contentSize = null;
 375:         if (w == LengthConstraintType.NONE) {
 376:             if (h == LengthConstraintType.NONE) {
 377:                 contentSize = new Size2D(getWidth(), getHeight()); 
 378:             }
 379:             else if (h == LengthConstraintType.RANGE) {
 380:                 throw new RuntimeException("Not yet implemented."); 
 381:             }
 382:             else if (h == LengthConstraintType.FIXED) {
 383:                 throw new RuntimeException("Not yet implemented.");
 384:             }            
 385:         }
 386:         else if (w == LengthConstraintType.RANGE) {
 387:             if (h == LengthConstraintType.NONE) {
 388:                 throw new RuntimeException("Not yet implemented."); 
 389:             }
 390:             else if (h == LengthConstraintType.RANGE) {
 391:                 contentSize = arrangeRR(g2, cc.getWidthRange(), 
 392:                         cc.getHeightRange()); 
 393:             }
 394:             else if (h == LengthConstraintType.FIXED) {
 395:                 throw new RuntimeException("Not yet implemented.");
 396:             }
 397:         }
 398:         else if (w == LengthConstraintType.FIXED) {
 399:             if (h == LengthConstraintType.NONE) {
 400:                 throw new RuntimeException("Not yet implemented."); 
 401:             }
 402:             else if (h == LengthConstraintType.RANGE) {
 403:                 throw new RuntimeException("Not yet implemented."); 
 404:             }
 405:             else if (h == LengthConstraintType.FIXED) {
 406:                 throw new RuntimeException("Not yet implemented.");
 407:             }
 408:         }
 409:         return new Size2D(calculateTotalWidth(contentSize.getWidth()),
 410:                 calculateTotalHeight(contentSize.getHeight()));
 411:     }
 412:     
 413:     /**
 414:      * Returns the content size for the title.  This will reflect the fact that
 415:      * a text title positioned on the left or right of a chart will be rotated
 416:      * 90 degrees.
 417:      * 
 418:      * @param g2  the graphics device.
 419:      * @param widthRange  the width range.
 420:      * @param heightRange  the height range.
 421:      * 
 422:      * @return The content size.
 423:      */
 424:     protected Size2D arrangeRR(Graphics2D g2, Range widthRange, 
 425:             Range heightRange) {
 426:         
 427:         RectangleEdge position = getPosition();
 428:         if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
 429:             
 430:             
 431:             float maxWidth = (float) widthRange.getUpperBound();
 432:             
 433:             // determine the space required for the axis
 434:             AxisSpace space = this.axis.reserveSpace(g2, null, 
 435:                     new Rectangle2D.Double(0, 0, maxWidth, 100), 
 436:                     RectangleEdge.BOTTOM, null);
 437:             
 438:             return new Size2D(maxWidth, this.stripWidth + this.axisOffset 
 439:                     + space.getTop() + space.getBottom());
 440:         }
 441:         else if (position == RectangleEdge.LEFT || position 
 442:                 == RectangleEdge.RIGHT) {
 443:             float maxHeight = (float) heightRange.getUpperBound();
 444:             AxisSpace space = this.axis.reserveSpace(g2, null, 
 445:                     new Rectangle2D.Double(0, 0, 100, maxHeight), 
 446:                     RectangleEdge.RIGHT, null);
 447:             return new Size2D(this.stripWidth + this.axisOffset 
 448:                     + space.getLeft() + space.getRight(), maxHeight);
 449:         }
 450:         else {
 451:             throw new RuntimeException("Unrecognised position.");
 452:         }
 453:     }
 454: 
 455:     /**
 456:      * Draws the legend within the specified area.
 457:      * 
 458:      * @param g2  the graphics target (<code>null</code> not permitted).
 459:      * @param area  the drawing area (<code>null</code> not permitted).
 460:      */
 461:     public void draw(Graphics2D g2, Rectangle2D area) {
 462:         draw(g2, area, null);
 463:     }
 464: 
 465:     /** 
 466:      * The number of subdivisions to use when drawing the paint strip.  Maybe
 467:      * this need to be user controllable? 
 468:      */
 469:     private static final int SUBDIVISIONS = 200;
 470:     
 471:     /**
 472:      * Draws the legend within the specified area.
 473:      * 
 474:      * @param g2  the graphics target (<code>null</code> not permitted).
 475:      * @param area  the drawing area (<code>null</code> not permitted).
 476:      * @param params  drawing parameters (ignored here).
 477:      * 
 478:      * @return <code>null</code>.
 479:      */
 480:     public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
 481:         
 482:         Rectangle2D target = (Rectangle2D) area.clone();
 483:         target = trimMargin(target);
 484:         if (this.backgroundPaint != null) {
 485:             g2.setPaint(this.backgroundPaint);
 486:             g2.fill(target);
 487:         }
 488:         getBorder().draw(g2, target);
 489:         getBorder().getInsets().trim(target);
 490:         target = trimPadding(target);
 491:         double base = this.axis.getLowerBound();
 492:         double increment = this.axis.getRange().getLength() / SUBDIVISIONS;
 493:         Rectangle2D r = new Rectangle2D.Double();
 494:         
 495:         
 496:         if (RectangleEdge.isTopOrBottom(getPosition())) {
 497:             RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
 498:                     this.axisLocation, PlotOrientation.HORIZONTAL);
 499:             double ww = Math.ceil(target.getWidth() / SUBDIVISIONS);
 500:             if (axisEdge == RectangleEdge.TOP) {
 501:                 for (int i = 0; i < SUBDIVISIONS; i++) {
 502:                     double v = base + (i * increment);
 503:                     Paint p = this.scale.getPaint(v);
 504:                     double vv = this.axis.valueToJava2D(v, target, 
 505:                             RectangleEdge.BOTTOM);
 506:                     r.setRect(vv, target.getMaxY() - this.stripWidth, ww, 
 507:                             this.stripWidth);
 508:                     g2.setPaint(p);
 509:                     g2.fill(r);                  
 510:                 }
 511:                 g2.setPaint(this.stripOutlinePaint);
 512:                 g2.setStroke(this.stripOutlineStroke);
 513:                 g2.draw(new Rectangle2D.Double(target.getMinX(), 
 514:                         target.getMaxY() - this.stripWidth, target.getWidth(), 
 515:                         this.stripWidth));
 516:                 this.axis.draw(g2, target.getMaxY() - this.stripWidth 
 517:                         - this.axisOffset, target, target, RectangleEdge.TOP, 
 518:                         null);                
 519:             }
 520:             else if (axisEdge == RectangleEdge.BOTTOM) {
 521:                 for (int i = 0; i < SUBDIVISIONS; i++) {
 522:                     double v = base + (i * increment);
 523:                     Paint p = this.scale.getPaint(v);
 524:                     double vv = this.axis.valueToJava2D(v, target, 
 525:                             RectangleEdge.BOTTOM);
 526:                     r.setRect(vv, target.getMinY(), ww, this.stripWidth);
 527:                     g2.setPaint(p);
 528:                     g2.fill(r);
 529:                 }
 530:                 g2.setPaint(this.stripOutlinePaint);
 531:                 g2.setStroke(this.stripOutlineStroke);
 532:                 g2.draw(new Rectangle2D.Double(target.getMinX(), 
 533:                         target.getMinY(), target.getWidth(), this.stripWidth));
 534:                 this.axis.draw(g2, target.getMinY() + this.stripWidth 
 535:                         + this.axisOffset, target, target, 
 536:                         RectangleEdge.BOTTOM, null);                
 537:             }
 538:         }
 539:         else {
 540:             RectangleEdge axisEdge = Plot.resolveRangeAxisLocation(
 541:                     this.axisLocation, PlotOrientation.VERTICAL);
 542:             double hh = Math.ceil(target.getHeight() / SUBDIVISIONS);
 543:             if (axisEdge == RectangleEdge.LEFT) {
 544:                 for (int i = 0; i < SUBDIVISIONS; i++) {
 545:                     double v = base + (i * increment);
 546:                     Paint p = this.scale.getPaint(v);
 547:                     double vv = this.axis.valueToJava2D(v, target, 
 548:                             RectangleEdge.LEFT);
 549:                     r.setRect(target.getMaxX() - this.stripWidth, vv - hh, 
 550:                             this.stripWidth, hh);
 551:                     g2.setPaint(p);
 552:                     g2.fill(r);
 553:                 }
 554:                 g2.setPaint(this.stripOutlinePaint);
 555:                 g2.setStroke(this.stripOutlineStroke);
 556:                 g2.draw(new Rectangle2D.Double(target.getMaxX() 
 557:                         - this.stripWidth, target.getMinY(), this.stripWidth, 
 558:                         target.getHeight()));
 559:                 this.axis.draw(g2, target.getMaxX() - this.stripWidth 
 560:                         - this.axisOffset, target, target, RectangleEdge.LEFT, 
 561:                         null);
 562:             }
 563:             else if (axisEdge == RectangleEdge.RIGHT) {
 564:                 for (int i = 0; i < SUBDIVISIONS; i++) {
 565:                     double v = base + (i * increment);
 566:                     Paint p = this.scale.getPaint(v);
 567:                     double vv = this.axis.valueToJava2D(v, target, 
 568:                             RectangleEdge.LEFT);
 569:                     r.setRect(target.getMinX(), vv - hh, this.stripWidth, hh);
 570:                     g2.setPaint(p);
 571:                     g2.fill(r);
 572:                 }
 573:                 g2.setPaint(this.stripOutlinePaint);
 574:                 g2.setStroke(this.stripOutlineStroke);
 575:                 g2.draw(new Rectangle2D.Double(target.getMinX(), 
 576:                         target.getMinY(), this.stripWidth, target.getHeight()));
 577:                 this.axis.draw(g2, target.getMinX() + this.stripWidth 
 578:                         + this.axisOffset, target, target, RectangleEdge.RIGHT,
 579:                         null);                
 580:             }
 581:         }
 582:         return null;
 583:     }
 584:     
 585:     /**
 586:      * Tests this legend for equality with an arbitrary object.
 587:      * 
 588:      * @param obj  the object (<code>null</code> permitted).
 589:      * 
 590:      * @return A boolean.
 591:      */
 592:     public boolean equals(Object obj) {
 593:         if (!(obj instanceof PaintScaleLegend)) {
 594:             return false;
 595:         }
 596:         PaintScaleLegend that = (PaintScaleLegend) obj;
 597:         if (!this.scale.equals(that.scale)) {
 598:             return false;
 599:         }
 600:         if (!this.axis.equals(that.axis)) {
 601:             return false;
 602:         }
 603:         if (!this.axisLocation.equals(that.axisLocation)) {
 604:             return false;
 605:         }
 606:         if (this.axisOffset != that.axisOffset) {
 607:             return false;
 608:         }
 609:         if (this.stripWidth != that.stripWidth) {
 610:             return false;
 611:         }
 612:         if (this.stripOutlineVisible != that.stripOutlineVisible) {
 613:             return false;
 614:         }
 615:         if (!PaintUtilities.equal(this.stripOutlinePaint, 
 616:                 that.stripOutlinePaint)) {
 617:             return false;
 618:         }
 619:         if (!this.stripOutlineStroke.equals(that.stripOutlineStroke)) {
 620:             return false;
 621:         }
 622:         if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
 623:             return false;
 624:         }
 625:         return super.equals(obj);
 626:     }
 627:     
 628:     /**
 629:      * Provides serialization support.
 630:      *
 631:      * @param stream  the output stream.
 632:      *
 633:      * @throws IOException  if there is an I/O error.
 634:      */
 635:     private void writeObject(ObjectOutputStream stream) throws IOException {
 636:         stream.defaultWriteObject();
 637:         SerialUtilities.writePaint(this.backgroundPaint, stream);
 638:         SerialUtilities.writePaint(this.stripOutlinePaint, stream);
 639:         SerialUtilities.writeStroke(this.stripOutlineStroke, stream);
 640:     }
 641: 
 642:     /**
 643:      * Provides serialization support.
 644:      *
 645:      * @param stream  the input stream.
 646:      *
 647:      * @throws IOException  if there is an I/O error.
 648:      * @throws ClassNotFoundException  if there is a classpath problem.
 649:      */
 650:     private void readObject(ObjectInputStream stream) 
 651:             throws IOException, ClassNotFoundException {
 652:         stream.defaultReadObject();
 653:         this.backgroundPaint = SerialUtilities.readPaint(stream);
 654:         this.stripOutlinePaint = SerialUtilities.readPaint(stream);
 655:         this.stripOutlineStroke = SerialUtilities.readStroke(stream);
 656:     }
 657: 
 658: }