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