Source for org.jfree.chart.title.LegendGraphic

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2006, 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:  * LegendGraphic.java
  29:  * ------------------
  30:  * (C) Copyright 2004-2006, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: LegendGraphic.java,v 1.9.2.4 2006/12/13 11:23:38 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 26-Oct-2004 : Version 1 (DG);
  40:  * 21-Jan-2005 : Modified return type of RectangleAnchor.coordinates() 
  41:  *               method (DG);
  42:  * 20-Apr-2005 : Added new draw() method (DG);
  43:  * 13-May-2005 : Fixed to respect margin, border and padding settings (DG);
  44:  * 01-Sep-2005 : Implemented PublicCloneable (DG);
  45:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  46:  * 13-Dec-2006 : Added fillPaintTransformer attribute, so legend graphics can
  47:  *               display gradient paint correctly, updated equals() and 
  48:  *               corrected clone() (DG);
  49:  * 
  50:  */
  51: 
  52: package org.jfree.chart.title;
  53: 
  54: import java.awt.GradientPaint;
  55: import java.awt.Graphics2D;
  56: import java.awt.Paint;
  57: import java.awt.Shape;
  58: import java.awt.Stroke;
  59: import java.awt.geom.Point2D;
  60: import java.awt.geom.Rectangle2D;
  61: import java.io.IOException;
  62: import java.io.ObjectInputStream;
  63: import java.io.ObjectOutputStream;
  64: 
  65: import org.jfree.chart.block.AbstractBlock;
  66: import org.jfree.chart.block.Block;
  67: import org.jfree.chart.block.LengthConstraintType;
  68: import org.jfree.chart.block.RectangleConstraint;
  69: import org.jfree.io.SerialUtilities;
  70: import org.jfree.ui.GradientPaintTransformer;
  71: import org.jfree.ui.RectangleAnchor;
  72: import org.jfree.ui.Size2D;
  73: import org.jfree.ui.StandardGradientPaintTransformer;
  74: import org.jfree.util.ObjectUtilities;
  75: import org.jfree.util.PaintUtilities;
  76: import org.jfree.util.PublicCloneable;
  77: import org.jfree.util.ShapeUtilities;
  78: 
  79: /**
  80:  * The graphical item within a legend item.
  81:  */
  82: public class LegendGraphic extends AbstractBlock 
  83:                            implements Block, PublicCloneable {
  84:     
  85:     /** 
  86:      * A flag that controls whether or not the shape is visible - see also 
  87:      * lineVisible. 
  88:      */
  89:     private boolean shapeVisible;
  90:     
  91:     /** 
  92:      * The shape to display.  To allow for accurate positioning, the center
  93:      * of the shape should be at (0, 0). 
  94:      */
  95:     private transient Shape shape;
  96:     
  97:     /**
  98:      * Defines the location within the block to which the shape will be aligned.
  99:      */
 100:     private RectangleAnchor shapeLocation;
 101:     
 102:     /** 
 103:      * Defines the point on the shape's bounding rectangle that will be 
 104:      * aligned to the drawing location when the shape is rendered.
 105:      */
 106:     private RectangleAnchor shapeAnchor;
 107:     
 108:     /** A flag that controls whether or not the shape is filled. */
 109:     private boolean shapeFilled;
 110:     
 111:     /** The fill paint for the shape. */
 112:     private transient Paint fillPaint;
 113:     
 114:     /**
 115:      * The fill paint transformer (used if the fillPaint is an instance of
 116:      * GradientPaint).
 117:      * 
 118:      * @since 1.0.4
 119:      */
 120:     private GradientPaintTransformer fillPaintTransformer;
 121:     
 122:     /** A flag that controls whether or not the shape outline is visible. */
 123:     private boolean shapeOutlineVisible;
 124:     
 125:     /** The outline paint for the shape. */
 126:     private transient Paint outlinePaint;
 127:     
 128:     /** The outline stroke for the shape. */
 129:     private transient Stroke outlineStroke;
 130:     
 131:     /** 
 132:      * A flag that controls whether or not the line is visible - see also 
 133:      * shapeVisible. 
 134:      */
 135:     private boolean lineVisible;
 136:     
 137:     /** The line. */
 138:     private transient Shape line;
 139:     
 140:     /** The line stroke. */
 141:     private transient Stroke lineStroke;
 142:     
 143:     /** The line paint. */
 144:     private transient Paint linePaint;
 145:     
 146:     /**
 147:      * Creates a new legend graphic.
 148:      * 
 149:      * @param shape  the shape (<code>null</code> not permitted).
 150:      * @param fillPaint  the fill paint (<code>null</code> not permitted).
 151:      */
 152:     public LegendGraphic(Shape shape, Paint fillPaint) {
 153:         if (shape == null) {
 154:             throw new IllegalArgumentException("Null 'shape' argument.");
 155:         }
 156:         if (fillPaint == null) {
 157:             throw new IllegalArgumentException("Null 'fillPaint' argument.");
 158:         }
 159:         this.shapeVisible = true;
 160:         this.shape = shape;
 161:         this.shapeAnchor = RectangleAnchor.CENTER;
 162:         this.shapeLocation = RectangleAnchor.CENTER;
 163:         this.shapeFilled = true;
 164:         this.fillPaint = fillPaint;
 165:         this.fillPaintTransformer = new StandardGradientPaintTransformer();
 166:         setPadding(2.0, 2.0, 2.0, 2.0);
 167:     }
 168:     
 169:     /**
 170:      * Returns a flag that controls whether or not the shape
 171:      * is visible.
 172:      * 
 173:      * @return A boolean.
 174:      */
 175:     public boolean isShapeVisible() {
 176:         return this.shapeVisible;
 177:     }
 178:     
 179:     /**
 180:      * Sets a flag that controls whether or not the shape is 
 181:      * visible.
 182:      * 
 183:      * @param visible  the flag.
 184:      */
 185:     public void setShapeVisible(boolean visible) {
 186:         this.shapeVisible = visible;
 187:     }
 188:     
 189:     /**
 190:      * Returns the shape.
 191:      * 
 192:      * @return The shape.
 193:      */
 194:     public Shape getShape() {
 195:         return this.shape;
 196:     }
 197:     
 198:     /**
 199:      * Sets the shape.
 200:      * 
 201:      * @param shape  the shape.
 202:      */
 203:     public void setShape(Shape shape) {
 204:         this.shape = shape;
 205:     }
 206: 
 207:     /**
 208:      * Returns a flag that controls whether or not the shapes
 209:      * are filled.
 210:      * 
 211:      * @return A boolean.
 212:      */
 213:     public boolean isShapeFilled() {
 214:         return this.shapeFilled;
 215:     }
 216:     
 217:     /**
 218:      * Sets a flag that controls whether or not the shape is
 219:      * filled.
 220:      * 
 221:      * @param filled  the flag.
 222:      */
 223:     public void setShapeFilled(boolean filled) {
 224:         this.shapeFilled = filled;
 225:     }
 226: 
 227:     /**
 228:      * Returns the paint used to fill the shape.
 229:      * 
 230:      * @return The fill paint.
 231:      */
 232:     public Paint getFillPaint() {
 233:         return this.fillPaint;
 234:     }
 235:     
 236:     /**
 237:      * Sets the paint used to fill the shape.
 238:      * 
 239:      * @param paint  the paint.
 240:      */
 241:     public void setFillPaint(Paint paint) {
 242:         this.fillPaint = paint;
 243:     }
 244:     
 245:     /**
 246:      * Returns the transformer used when the fill paint is an instance of 
 247:      * <code>GradientPaint</code>.
 248:      * 
 249:      * @return The transformer (never <code>null</code>).
 250:      * 
 251:      * @since 1.0.4.
 252:      */
 253:     public GradientPaintTransformer getFillPaintTransformer() {
 254:         return this.fillPaintTransformer;
 255:     }
 256:     
 257:     /**
 258:      * Sets the transformer used when the fill paint is an instance of 
 259:      * <code>GradientPaint</code>.
 260:      * 
 261:      * @param transformer  the transformer (<code>null</code> not permitted).
 262:      * 
 263:      * @since 1.0.4
 264:      */
 265:     public void setFillPaintTransformer(GradientPaintTransformer transformer) {
 266:         if (transformer == null) {
 267:             throw new IllegalArgumentException("Null 'transformer' argument.");
 268:         }
 269:         this.fillPaintTransformer = transformer;
 270:     }
 271:     
 272:     /**
 273:      * Returns a flag that controls whether the shape outline is visible.
 274:      * 
 275:      * @return A boolean.
 276:      */
 277:     public boolean isShapeOutlineVisible() {
 278:         return this.shapeOutlineVisible;
 279:     }
 280:     
 281:     /**
 282:      * Sets a flag that controls whether or not the shape outline
 283:      * is visible.
 284:      * 
 285:      * @param visible  the flag.
 286:      */
 287:     public void setShapeOutlineVisible(boolean visible) {
 288:         this.shapeOutlineVisible = visible;
 289:     }
 290:     
 291:     /**
 292:      * Returns the outline paint.
 293:      * 
 294:      * @return The paint.
 295:      */
 296:     public Paint getOutlinePaint() {
 297:         return this.outlinePaint;
 298:     }
 299:     
 300:     /**
 301:      * Sets the outline paint.
 302:      * 
 303:      * @param paint  the paint.
 304:      */
 305:     public void setOutlinePaint(Paint paint) {
 306:         this.outlinePaint = paint;
 307:     }
 308: 
 309:     /**
 310:      * Returns the outline stroke.
 311:      * 
 312:      * @return The stroke.
 313:      */
 314:     public Stroke getOutlineStroke() {
 315:         return this.outlineStroke;
 316:     }
 317:     
 318:     /**
 319:      * Sets the outline stroke.
 320:      * 
 321:      * @param stroke  the stroke.
 322:      */
 323:     public void setOutlineStroke(Stroke stroke) {
 324:         this.outlineStroke = stroke;
 325:     }
 326: 
 327:     /**
 328:      * Returns the shape anchor.
 329:      * 
 330:      * @return The shape anchor.
 331:      */
 332:     public RectangleAnchor getShapeAnchor() {
 333:         return this.shapeAnchor;
 334:     }
 335:     
 336:     /**
 337:      * Sets the shape anchor.  This defines a point on the shapes bounding
 338:      * rectangle that will be used to align the shape to a location.
 339:      * 
 340:      * @param anchor  the anchor (<code>null</code> not permitted).
 341:      */
 342:     public void setShapeAnchor(RectangleAnchor anchor) {
 343:         if (anchor == null) {
 344:             throw new IllegalArgumentException("Null 'anchor' argument.");
 345:         }
 346:         this.shapeAnchor = anchor;    
 347:     }
 348:     
 349:     /**
 350:      * Returns the shape location.
 351:      * 
 352:      * @return The shape location.
 353:      */
 354:     public RectangleAnchor getShapeLocation() {
 355:         return this.shapeLocation;
 356:     }
 357:     
 358:     /**
 359:      * Sets the shape location.  This defines a point within the drawing
 360:      * area that will be used to align the shape to.
 361:      * 
 362:      * @param location  the location (<code>null</code> not permitted).
 363:      */
 364:     public void setShapeLocation(RectangleAnchor location) {
 365:         if (location == null) {
 366:             throw new IllegalArgumentException("Null 'location' argument.");
 367:         }
 368:         this.shapeLocation = location;
 369:     }
 370:     
 371:     /**
 372:      * Returns the flag that controls whether or not the line is visible.
 373:      * 
 374:      * @return A boolean.
 375:      */
 376:     public boolean isLineVisible() {
 377:         return this.lineVisible;
 378:     }
 379:     
 380:     /**
 381:      * Sets the flag that controls whether or not the line is visible.
 382:      * 
 383:      * @param visible  the flag.
 384:      */
 385:     public void setLineVisible(boolean visible) {
 386:         this.lineVisible = visible;
 387:     }
 388: 
 389:     /**
 390:      * Returns the line centered about (0, 0).
 391:      * 
 392:      * @return The line.
 393:      */
 394:     public Shape getLine() {
 395:         return this.line;
 396:     }
 397:     
 398:     /**
 399:      * Sets the line.  A Shape is used here, because then you can use Line2D, 
 400:      * GeneralPath or any other Shape to represent the line.
 401:      * 
 402:      * @param line  the line.
 403:      */
 404:     public void setLine(Shape line) {
 405:         this.line = line;
 406:     }
 407:     
 408:     /**
 409:      * Returns the line paint.
 410:      * 
 411:      * @return The paint.
 412:      */
 413:     public Paint getLinePaint() {
 414:         return this.linePaint;
 415:     }
 416:     
 417:     /**
 418:      * Sets the line paint.
 419:      * 
 420:      * @param paint  the paint.
 421:      */
 422:     public void setLinePaint(Paint paint) {
 423:         this.linePaint = paint;
 424:     }
 425:     
 426:     /**
 427:      * Returns the line stroke.
 428:      * 
 429:      * @return The stroke.
 430:      */
 431:     public Stroke getLineStroke() {
 432:         return this.lineStroke;
 433:     }
 434:     
 435:     /**
 436:      * Sets the line stroke.
 437:      * 
 438:      * @param stroke  the stroke.
 439:      */
 440:     public void setLineStroke(Stroke stroke) {
 441:         this.lineStroke = stroke;
 442:     }
 443:     
 444:     /**
 445:      * Arranges the contents of the block, within the given constraints, and 
 446:      * returns the block size.
 447:      * 
 448:      * @param g2  the graphics device.
 449:      * @param constraint  the constraint (<code>null</code> not permitted).
 450:      * 
 451:      * @return The block size (in Java2D units, never <code>null</code>).
 452:      */
 453:     public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
 454:         RectangleConstraint contentConstraint = toContentConstraint(constraint);
 455:         LengthConstraintType w = contentConstraint.getWidthConstraintType();
 456:         LengthConstraintType h = contentConstraint.getHeightConstraintType();
 457:         Size2D contentSize = null;
 458:         if (w == LengthConstraintType.NONE) {
 459:             if (h == LengthConstraintType.NONE) {
 460:                 contentSize = arrangeNN(g2);
 461:             }
 462:             else if (h == LengthConstraintType.RANGE) {
 463:                 throw new RuntimeException("Not yet implemented.");
 464:             }
 465:             else if (h == LengthConstraintType.FIXED) {
 466:                 throw new RuntimeException("Not yet implemented.");
 467:             }
 468:         }
 469:         else if (w == LengthConstraintType.RANGE) {
 470:             if (h == LengthConstraintType.NONE) {
 471:                 throw new RuntimeException("Not yet implemented.");
 472:             }
 473:             else if (h == LengthConstraintType.RANGE) {
 474:                 throw new RuntimeException("Not yet implemented.");
 475:             }
 476:             else if (h == LengthConstraintType.FIXED) {
 477:                 throw new RuntimeException("Not yet implemented.");
 478:             }
 479:         }
 480:         else if (w == LengthConstraintType.FIXED) {
 481:             if (h == LengthConstraintType.NONE) {
 482:                 throw new RuntimeException("Not yet implemented.");
 483:             }
 484:             else if (h == LengthConstraintType.RANGE) {
 485:                 throw new RuntimeException("Not yet implemented.");
 486:             }
 487:             else if (h == LengthConstraintType.FIXED) {   
 488:                 contentSize = new Size2D(
 489:                     contentConstraint.getWidth(),
 490:                     contentConstraint.getHeight()
 491:                 );
 492:             }            
 493:         }
 494:         return new Size2D(
 495:             calculateTotalWidth(contentSize.getWidth()), 
 496:             calculateTotalHeight(contentSize.getHeight())
 497:         );
 498:     }
 499:     
 500:     /**
 501:      * Performs the layout with no constraint, so the content size is 
 502:      * determined by the bounds of the shape and/or line drawn to represent 
 503:      * the series.
 504:      * 
 505:      * @param g2  the graphics device.
 506:      * 
 507:      * @return  The content size.
 508:      */
 509:     protected Size2D arrangeNN(Graphics2D g2) {
 510:         Rectangle2D contentSize = new Rectangle2D.Double();
 511:         if (this.line != null) {
 512:             contentSize.setRect(this.line.getBounds2D());
 513:         }
 514:         if (this.shape != null) {
 515:             contentSize = contentSize.createUnion(this.shape.getBounds2D());
 516:         }
 517:         return new Size2D(contentSize.getWidth(), contentSize.getHeight());
 518:     }
 519: 
 520:     /**
 521:      * Draws the graphic item within the specified area.
 522:      * 
 523:      * @param g2  the graphics device.
 524:      * @param area  the area.
 525:      */
 526:     public void draw(Graphics2D g2, Rectangle2D area) {
 527:         
 528:         area = trimMargin(area);
 529:         drawBorder(g2, area);
 530:         area = trimBorder(area);
 531:         area = trimPadding(area);
 532:         
 533:         if (this.lineVisible) {
 534:             Point2D location = RectangleAnchor.coordinates(
 535:                 area, this.shapeLocation
 536:             );
 537:             Shape aLine = ShapeUtilities.createTranslatedShape(
 538:                 getLine(), this.shapeAnchor, location.getX(), location.getY()
 539:             );
 540:             g2.setPaint(this.linePaint);
 541:             g2.setStroke(this.lineStroke);
 542:             g2.draw(aLine);
 543:         }
 544:         
 545:         if (this.shapeVisible) {
 546:             Point2D location = RectangleAnchor.coordinates(
 547:                 area, this.shapeLocation
 548:             );
 549:             
 550:             Shape s = ShapeUtilities.createTranslatedShape(
 551:                 this.shape, this.shapeAnchor, location.getX(), location.getY()
 552:             );
 553:             if (this.shapeFilled) {
 554:                 Paint p = this.fillPaint;
 555:                 if (p instanceof GradientPaint) {
 556:                     GradientPaint gp = (GradientPaint) this.fillPaint;
 557:                     p = this.fillPaintTransformer.transform(gp, s);
 558:                 }
 559:                 g2.setPaint(p);
 560:                 g2.fill(s);
 561:             }
 562:             if (this.shapeOutlineVisible) {
 563:                 g2.setPaint(this.outlinePaint);
 564:                 g2.setStroke(this.outlineStroke);
 565:                 g2.draw(s);
 566:             }
 567:         }
 568:         
 569:     }
 570:     
 571:     /**
 572:      * Draws the block within the specified area.
 573:      * 
 574:      * @param g2  the graphics device.
 575:      * @param area  the area.
 576:      * @param params  ignored (<code>null</code> permitted).
 577:      * 
 578:      * @return Always <code>null</code>.
 579:      */
 580:     public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
 581:         draw(g2, area);
 582:         return null;
 583:     }
 584:     
 585:     /**
 586:      * Tests this <code>LegendGraphic</code> instance for equality with an
 587:      * arbitrary object.
 588:      * 
 589:      * @param obj  the object (<code>null</code> permitted).
 590:      * 
 591:      * @return A boolean.
 592:      */
 593:     public boolean equals(Object obj) {
 594:         if (!(obj instanceof LegendGraphic)) {
 595:             return false;
 596:         }
 597:         LegendGraphic that = (LegendGraphic) obj;
 598:         if (this.shapeVisible != that.shapeVisible) {
 599:             return false;
 600:         }
 601:         if (!ShapeUtilities.equal(this.shape, that.shape)) {
 602:             return false;
 603:         }
 604:         if (this.shapeFilled != that.shapeFilled) {
 605:             return false;
 606:         }
 607:         if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) {
 608:             return false;
 609:         }
 610:         if (!ObjectUtilities.equal(this.fillPaintTransformer, 
 611:                 that.fillPaintTransformer)) {
 612:             return false;
 613:         }
 614:         if (this.shapeOutlineVisible != that.shapeOutlineVisible) {
 615:             return false;
 616:         }
 617:         if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
 618:             return false;
 619:         }
 620:         if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) {
 621:             return false;
 622:         }
 623:         if (this.shapeAnchor != that.shapeAnchor) {
 624:             return false;
 625:         }
 626:         if (this.shapeLocation != that.shapeLocation) {
 627:             return false;
 628:         }
 629:         if (this.lineVisible != that.lineVisible) {
 630:             return false;
 631:         }
 632:         if (!ShapeUtilities.equal(this.line, that.line)) {
 633:             return false;
 634:         }
 635:         if (!PaintUtilities.equal(this.linePaint, that.linePaint)) {
 636:             return false;
 637:         }
 638:         if (!ObjectUtilities.equal(this.lineStroke, that.lineStroke)) {
 639:             return false;
 640:         }
 641:         return super.equals(obj);    
 642:     }
 643:     
 644:     /**
 645:      * Returns a hash code for this instance.
 646:      * 
 647:      * @return A hash code.
 648:      */
 649:     public int hashCode() {
 650:         int result = 193;   
 651:         result = 37 * result + ObjectUtilities.hashCode(this.fillPaint);
 652:         // FIXME: use other fields too
 653:         return result;
 654:     }
 655:     
 656:     /**
 657:      * Returns a clone of this <code>LegendGraphic</code> instance.
 658:      * 
 659:      * @return A clone of this <code>LegendGraphic</code> instance.
 660:      * 
 661:      * @throws CloneNotSupportedException if there is a problem cloning.
 662:      */
 663:     public Object clone() throws CloneNotSupportedException {
 664:         LegendGraphic clone = (LegendGraphic) super.clone();
 665:         clone.shape = ShapeUtilities.clone(this.shape);
 666:         clone.line = ShapeUtilities.clone(this.line);
 667:         return clone;
 668:     }
 669:     
 670:     /**
 671:      * Provides serialization support.
 672:      *
 673:      * @param stream  the output stream.
 674:      *
 675:      * @throws IOException  if there is an I/O error.
 676:      */
 677:     private void writeObject(ObjectOutputStream stream) throws IOException {
 678:         stream.defaultWriteObject();
 679:         SerialUtilities.writeShape(this.shape, stream);
 680:         SerialUtilities.writePaint(this.fillPaint, stream);
 681:         SerialUtilities.writePaint(this.outlinePaint, stream);
 682:         SerialUtilities.writeStroke(this.outlineStroke, stream);
 683:         SerialUtilities.writeShape(this.line, stream);
 684:         SerialUtilities.writePaint(this.linePaint, stream);
 685:         SerialUtilities.writeStroke(this.lineStroke, stream);
 686:     }
 687: 
 688:     /**
 689:      * Provides serialization support.
 690:      *
 691:      * @param stream  the input stream.
 692:      *
 693:      * @throws IOException  if there is an I/O error.
 694:      * @throws ClassNotFoundException  if there is a classpath problem.
 695:      */
 696:     private void readObject(ObjectInputStream stream) 
 697:         throws IOException, ClassNotFoundException 
 698:     {
 699:         stream.defaultReadObject();
 700:         this.shape = SerialUtilities.readShape(stream);
 701:         this.fillPaint = SerialUtilities.readPaint(stream);
 702:         this.outlinePaint = SerialUtilities.readPaint(stream);
 703:         this.outlineStroke = SerialUtilities.readStroke(stream);
 704:         this.line = SerialUtilities.readShape(stream);
 705:         this.linePaint = SerialUtilities.readPaint(stream);
 706:         this.lineStroke = SerialUtilities.readStroke(stream);
 707:     }
 708: 
 709: }