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