Source for org.jfree.chart.renderer.xy.XYLineAndShapeRenderer

   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:  * XYLineAndShapeRenderer.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:  * 27-Jan-2004 : Version 1 (DG);
  38:  * 10-Feb-2004 : Minor change to drawItem() method to make cut-and-paste 
  39:  *               overriding easier (DG);
  40:  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
  41:  * 25-Aug-2004 : Added support for chart entities (required for tooltips) (DG);
  42:  * 24-Sep-2004 : Added flag to allow whole series to be drawn as a path 
  43:  *               (necessary when using a dashed stroke with many data 
  44:  *               items) (DG);
  45:  * 04-Oct-2004 : Renamed BooleanUtils --> BooleanUtilities (DG);
  46:  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
  47:  * 27-Jan-2005 : The getLegendItem() method now omits hidden series (DG);
  48:  * 28-Jan-2005 : Added new constructor (DG);
  49:  * 09-Mar-2005 : Added fillPaint settings (DG);
  50:  * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG);
  51:  * 22-Jul-2005 : Renamed defaultLinesVisible --> baseLinesVisible, 
  52:  *               defaultShapesVisible --> baseShapesVisible and
  53:  *               defaultShapesFilled --> baseShapesFilled (DG);
  54:  * 29-Jul-2005 : Added code to draw item labels (DG);
  55:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  56:  * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
  57:  * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG);
  58:  * 21-Feb-2007 : Fixed bugs in clone() and equals() (DG);
  59:  * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
  60:  * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
  61:  * 08-Jun-2007 : Fix for bug 1731912 where entities are created even for data
  62:  *               items that are not displayed (DG);
  63:  * 26-Oct-2007 : Deprecated override attributes (DG);
  64:  *
  65:  */
  66: 
  67: package org.jfree.chart.renderer.xy;
  68: 
  69: import java.awt.Graphics2D;
  70: import java.awt.Paint;
  71: import java.awt.Shape;
  72: import java.awt.Stroke;
  73: import java.awt.geom.GeneralPath;
  74: import java.awt.geom.Line2D;
  75: import java.awt.geom.Rectangle2D;
  76: import java.io.IOException;
  77: import java.io.ObjectInputStream;
  78: import java.io.ObjectOutputStream;
  79: import java.io.Serializable;
  80: 
  81: import org.jfree.chart.LegendItem;
  82: import org.jfree.chart.axis.ValueAxis;
  83: import org.jfree.chart.entity.EntityCollection;
  84: import org.jfree.chart.event.RendererChangeEvent;
  85: import org.jfree.chart.plot.CrosshairState;
  86: import org.jfree.chart.plot.PlotOrientation;
  87: import org.jfree.chart.plot.PlotRenderingInfo;
  88: import org.jfree.chart.plot.XYPlot;
  89: import org.jfree.data.xy.XYDataset;
  90: import org.jfree.io.SerialUtilities;
  91: import org.jfree.ui.RectangleEdge;
  92: import org.jfree.util.BooleanList;
  93: import org.jfree.util.BooleanUtilities;
  94: import org.jfree.util.ObjectUtilities;
  95: import org.jfree.util.PublicCloneable;
  96: import org.jfree.util.ShapeUtilities;
  97: 
  98: /**
  99:  * A renderer that connects data points with lines and/or draws shapes at each
 100:  * data point.  This renderer is designed for use with the {@link XYPlot} 
 101:  * class.
 102:  */
 103: public class XYLineAndShapeRenderer extends AbstractXYItemRenderer 
 104:                                     implements XYItemRenderer, 
 105:                                                Cloneable,
 106:                                                PublicCloneable,
 107:                                                Serializable {
 108: 
 109:     /** For serialization. */
 110:     private static final long serialVersionUID = -7435246895986425885L;
 111:     
 112:     /** 
 113:      * A flag that controls whether or not lines are visible for ALL series. 
 114:      * 
 115:      * @deprecated As of 1.0.7.
 116:      */
 117:     private Boolean linesVisible;
 118: 
 119:     /** 
 120:      * A table of flags that control (per series) whether or not lines are 
 121:      * visible. 
 122:      */
 123:     private BooleanList seriesLinesVisible;
 124: 
 125:     /** The default value returned by the getLinesVisible() method. */
 126:     private boolean baseLinesVisible;
 127: 
 128:     /** The shape that is used to represent a line in the legend. */
 129:     private transient Shape legendLine;
 130:     
 131:     /** 
 132:      * A flag that controls whether or not shapes are visible for ALL series.
 133:      * 
 134:      * @deprecated As of 1.0.7.
 135:      */
 136:     private Boolean shapesVisible;
 137: 
 138:     /** 
 139:      * A table of flags that control (per series) whether or not shapes are 
 140:      * visible. 
 141:      */
 142:     private BooleanList seriesShapesVisible;
 143: 
 144:     /** The default value returned by the getShapeVisible() method. */
 145:     private boolean baseShapesVisible;
 146: 
 147:     /** 
 148:      * A flag that controls whether or not shapes are filled for ALL series. 
 149:      * 
 150:      * @deprecated As of 1.0.7.
 151:      */
 152:     private Boolean shapesFilled;
 153: 
 154:     /** 
 155:      * A table of flags that control (per series) whether or not shapes are 
 156:      * filled. 
 157:      */
 158:     private BooleanList seriesShapesFilled;
 159: 
 160:     /** The default value returned by the getShapeFilled() method. */
 161:     private boolean baseShapesFilled;
 162:     
 163:     /** A flag that controls whether outlines are drawn for shapes. */
 164:     private boolean drawOutlines;
 165:     
 166:     /** 
 167:      * A flag that controls whether the fill paint is used for filling 
 168:      * shapes. 
 169:      */
 170:     private boolean useFillPaint;
 171:     
 172:     /** 
 173:      * A flag that controls whether the outline paint is used for drawing shape 
 174:      * outlines. 
 175:      */
 176:     private boolean useOutlinePaint;
 177:     
 178:     /** 
 179:      * A flag that controls whether or not each series is drawn as a single 
 180:      * path. 
 181:      */
 182:     private boolean drawSeriesLineAsPath;
 183: 
 184:     /**
 185:      * Creates a new renderer with both lines and shapes visible.
 186:      */
 187:     public XYLineAndShapeRenderer() {
 188:         this(true, true);
 189:     }
 190:     
 191:     /**
 192:      * Creates a new renderer.
 193:      * 
 194:      * @param lines  lines visible?
 195:      * @param shapes  shapes visible?
 196:      */
 197:     public XYLineAndShapeRenderer(boolean lines, boolean shapes) {
 198:         this.linesVisible = null;
 199:         this.seriesLinesVisible = new BooleanList();
 200:         this.baseLinesVisible = lines;
 201:         this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0);
 202:         
 203:         this.shapesVisible = null;
 204:         this.seriesShapesVisible = new BooleanList();
 205:         this.baseShapesVisible = shapes;
 206:         
 207:         this.shapesFilled = null;
 208:         this.useFillPaint = false;     // use item paint for fills by default
 209:         this.seriesShapesFilled = new BooleanList();
 210:         this.baseShapesFilled = true;
 211: 
 212:         this.drawOutlines = true;     
 213:         this.useOutlinePaint = false;  // use item paint for outlines by 
 214:                                        // default, not outline paint
 215:         
 216:         this.drawSeriesLineAsPath = false;
 217:     }
 218:     
 219:     /**
 220:      * Returns a flag that controls whether or not each series is drawn as a 
 221:      * single path.
 222:      * 
 223:      * @return A boolean.
 224:      * 
 225:      * @see #setDrawSeriesLineAsPath(boolean)
 226:      */
 227:     public boolean getDrawSeriesLineAsPath() {
 228:         return this.drawSeriesLineAsPath;
 229:     }
 230:     
 231:     /**
 232:      * Sets the flag that controls whether or not each series is drawn as a 
 233:      * single path and sends a {@link RendererChangeEvent} to all registered
 234:      * listeners.
 235:      * 
 236:      * @param flag  the flag.
 237:      * 
 238:      * @see #getDrawSeriesLineAsPath()
 239:      */
 240:     public void setDrawSeriesLineAsPath(boolean flag) {
 241:         if (this.drawSeriesLineAsPath != flag) {
 242:             this.drawSeriesLineAsPath = flag;
 243:             fireChangeEvent();
 244:         }
 245:     }
 246:     
 247:     /**
 248:      * Returns the number of passes through the data that the renderer requires 
 249:      * in order to draw the chart.  Most charts will require a single pass, but 
 250:      * some require two passes.
 251:      * 
 252:      * @return The pass count.
 253:      */
 254:     public int getPassCount() {
 255:         return 2;
 256:     }
 257:     
 258:     // LINES VISIBLE
 259: 
 260:     /**
 261:      * Returns the flag used to control whether or not the shape for an item is 
 262:      * visible.
 263:      *
 264:      * @param series  the series index (zero-based).
 265:      * @param item  the item index (zero-based).
 266:      *
 267:      * @return A boolean.
 268:      */
 269:     public boolean getItemLineVisible(int series, int item) {
 270:         Boolean flag = this.linesVisible;
 271:         if (flag == null) {
 272:             flag = getSeriesLinesVisible(series);
 273:         }
 274:         if (flag != null) {
 275:             return flag.booleanValue();
 276:         }
 277:         else {
 278:             return this.baseLinesVisible;   
 279:         }
 280:     }
 281: 
 282:     /**
 283:      * Returns a flag that controls whether or not lines are drawn for ALL 
 284:      * series.  If this flag is <code>null</code>, then the "per series" 
 285:      * settings will apply.
 286:      * 
 287:      * @return A flag (possibly <code>null</code>).
 288:      * 
 289:      * @see #setLinesVisible(Boolean)
 290:      * 
 291:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 292:      */
 293:     public Boolean getLinesVisible() {
 294:         return this.linesVisible;   
 295:     }
 296:     
 297:     /**
 298:      * Sets a flag that controls whether or not lines are drawn between the 
 299:      * items in ALL series, and sends a {@link RendererChangeEvent} to all 
 300:      * registered listeners.  You need to set this to <code>null</code> if you 
 301:      * want the "per series" settings to apply.
 302:      *
 303:      * @param visible  the flag (<code>null</code> permitted).
 304:      * 
 305:      * @see #getLinesVisible()
 306:      * 
 307:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 308:      */
 309:     public void setLinesVisible(Boolean visible) {
 310:         this.linesVisible = visible;
 311:         fireChangeEvent();
 312:     }
 313: 
 314:     /**
 315:      * Sets a flag that controls whether or not lines are drawn between the 
 316:      * items in ALL series, and sends a {@link RendererChangeEvent} to all 
 317:      * registered listeners.
 318:      *
 319:      * @param visible  the flag.
 320:      * 
 321:      * @see #getLinesVisible()
 322:      * 
 323:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 324:      */
 325:     public void setLinesVisible(boolean visible) {
 326:         // we use BooleanUtilities here to preserve JRE 1.3.1 compatibility
 327:         setLinesVisible(BooleanUtilities.valueOf(visible));
 328:     }
 329: 
 330:     /**
 331:      * Returns the flag used to control whether or not the lines for a series 
 332:      * are visible.
 333:      *
 334:      * @param series  the series index (zero-based).
 335:      *
 336:      * @return The flag (possibly <code>null</code>).
 337:      * 
 338:      * @see #setSeriesLinesVisible(int, Boolean)
 339:      */
 340:     public Boolean getSeriesLinesVisible(int series) {
 341:         return this.seriesLinesVisible.getBoolean(series);
 342:     }
 343: 
 344:     /**
 345:      * Sets the 'lines visible' flag for a series and sends a 
 346:      * {@link RendererChangeEvent} to all registered listeners.
 347:      *
 348:      * @param series  the series index (zero-based).
 349:      * @param flag  the flag (<code>null</code> permitted).
 350:      * 
 351:      * @see #getSeriesLinesVisible(int)
 352:      */
 353:     public void setSeriesLinesVisible(int series, Boolean flag) {
 354:         this.seriesLinesVisible.setBoolean(series, flag);
 355:         fireChangeEvent();
 356:     }
 357: 
 358:     /**
 359:      * Sets the 'lines visible' flag for a series and sends a 
 360:      * {@link RendererChangeEvent} to all registered listeners.
 361:      * 
 362:      * @param series  the series index (zero-based).
 363:      * @param visible  the flag.
 364:      * 
 365:      * @see #getSeriesLinesVisible(int)
 366:      */
 367:     public void setSeriesLinesVisible(int series, boolean visible) {
 368:         setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
 369:     }
 370:     
 371:     /**
 372:      * Returns the base 'lines visible' attribute.
 373:      *
 374:      * @return The base flag.
 375:      * 
 376:      * @see #setBaseLinesVisible(boolean)
 377:      */
 378:     public boolean getBaseLinesVisible() {
 379:         return this.baseLinesVisible;
 380:     }
 381: 
 382:     /**
 383:      * Sets the base 'lines visible' flag and sends a 
 384:      * {@link RendererChangeEvent} to all registered listeners.
 385:      *
 386:      * @param flag  the flag.
 387:      * 
 388:      * @see #getBaseLinesVisible()
 389:      */
 390:     public void setBaseLinesVisible(boolean flag) {
 391:         this.baseLinesVisible = flag;
 392:         fireChangeEvent();
 393:     }
 394: 
 395:     /**
 396:      * Returns the shape used to represent a line in the legend.
 397:      * 
 398:      * @return The legend line (never <code>null</code>).
 399:      * 
 400:      * @see #setLegendLine(Shape)
 401:      */
 402:     public Shape getLegendLine() {
 403:         return this.legendLine;   
 404:     }
 405:     
 406:     /**
 407:      * Sets the shape used as a line in each legend item and sends a 
 408:      * {@link RendererChangeEvent} to all registered listeners.
 409:      * 
 410:      * @param line  the line (<code>null</code> not permitted).
 411:      * 
 412:      * @see #getLegendLine()
 413:      */
 414:     public void setLegendLine(Shape line) {
 415:         if (line == null) {
 416:             throw new IllegalArgumentException("Null 'line' argument.");   
 417:         }
 418:         this.legendLine = line;
 419:         fireChangeEvent();
 420:     }
 421: 
 422:     // SHAPES VISIBLE
 423: 
 424:     /**
 425:      * Returns the flag used to control whether or not the shape for an item is
 426:      * visible.
 427:      * <p>
 428:      * The default implementation passes control to the 
 429:      * <code>getSeriesShapesVisible</code> method. You can override this method
 430:      * if you require different behaviour.
 431:      *
 432:      * @param series  the series index (zero-based).
 433:      * @param item  the item index (zero-based).
 434:      *
 435:      * @return A boolean.
 436:      */
 437:     public boolean getItemShapeVisible(int series, int item) {
 438:         Boolean flag = this.shapesVisible;
 439:         if (flag == null) {
 440:             flag = getSeriesShapesVisible(series);
 441:         }
 442:         if (flag != null) {
 443:             return flag.booleanValue();   
 444:         }
 445:         else {
 446:             return this.baseShapesVisible;
 447:         }
 448:     }
 449: 
 450:     /**
 451:      * Returns the flag that controls whether the shapes are visible for the 
 452:      * items in ALL series.
 453:      * 
 454:      * @return The flag (possibly <code>null</code>).
 455:      * 
 456:      * @see #setShapesVisible(Boolean)
 457:      * 
 458:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 459:      */
 460:     public Boolean getShapesVisible() {
 461:         return this.shapesVisible;    
 462:     }
 463:     
 464:     /**
 465:      * Sets the 'shapes visible' for ALL series and sends a 
 466:      * {@link RendererChangeEvent} to all registered listeners.
 467:      *
 468:      * @param visible  the flag (<code>null</code> permitted).
 469:      * 
 470:      * @see #getShapesVisible()
 471:      * 
 472:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 473:      */
 474:     public void setShapesVisible(Boolean visible) {
 475:         this.shapesVisible = visible;
 476:         fireChangeEvent();
 477:     }
 478: 
 479:     /**
 480:      * Sets the 'shapes visible' for ALL series and sends a 
 481:      * {@link RendererChangeEvent} to all registered listeners.
 482:      * 
 483:      * @param visible  the flag.
 484:      * 
 485:      * @see #getShapesVisible()
 486:      * 
 487:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 488:      */
 489:     public void setShapesVisible(boolean visible) {
 490:         setShapesVisible(BooleanUtilities.valueOf(visible));
 491:     }
 492: 
 493:     /**
 494:      * Returns the flag used to control whether or not the shapes for a series
 495:      * are visible.
 496:      *
 497:      * @param series  the series index (zero-based).
 498:      *
 499:      * @return A boolean.
 500:      * 
 501:      * @see #setSeriesShapesVisible(int, Boolean)
 502:      */
 503:     public Boolean getSeriesShapesVisible(int series) {
 504:         return this.seriesShapesVisible.getBoolean(series);
 505:     }
 506: 
 507:     /**
 508:      * Sets the 'shapes visible' flag for a series and sends a 
 509:      * {@link RendererChangeEvent} to all registered listeners.
 510:      * 
 511:      * @param series  the series index (zero-based).
 512:      * @param visible  the flag.
 513:      * 
 514:      * @see #getSeriesShapesVisible(int)
 515:      */
 516:     public void setSeriesShapesVisible(int series, boolean visible) {
 517:         setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
 518:     }
 519:     
 520:     /**
 521:      * Sets the 'shapes visible' flag for a series and sends a 
 522:      * {@link RendererChangeEvent} to all registered listeners.
 523:      *
 524:      * @param series  the series index (zero-based).
 525:      * @param flag  the flag.
 526:      * 
 527:      * @see #getSeriesShapesVisible(int)
 528:      */
 529:     public void setSeriesShapesVisible(int series, Boolean flag) {
 530:         this.seriesShapesVisible.setBoolean(series, flag);
 531:         fireChangeEvent();
 532:     }
 533: 
 534:     /**
 535:      * Returns the base 'shape visible' attribute.
 536:      *
 537:      * @return The base flag.
 538:      * 
 539:      * @see #setBaseShapesVisible(boolean)
 540:      */
 541:     public boolean getBaseShapesVisible() {
 542:         return this.baseShapesVisible;
 543:     }
 544: 
 545:     /**
 546:      * Sets the base 'shapes visible' flag and sends a 
 547:      * {@link RendererChangeEvent} to all registered listeners.
 548:      *
 549:      * @param flag  the flag.
 550:      * 
 551:      * @see #getBaseShapesVisible()
 552:      */
 553:     public void setBaseShapesVisible(boolean flag) {
 554:         this.baseShapesVisible = flag;
 555:         fireChangeEvent();
 556:     }
 557: 
 558:     // SHAPES FILLED
 559: 
 560:     /**
 561:      * Returns the flag used to control whether or not the shape for an item 
 562:      * is filled.
 563:      * <p>
 564:      * The default implementation passes control to the 
 565:      * <code>getSeriesShapesFilled</code> method. You can override this method
 566:      * if you require different behaviour.
 567:      *
 568:      * @param series  the series index (zero-based).
 569:      * @param item  the item index (zero-based).
 570:      *
 571:      * @return A boolean.
 572:      */
 573:     public boolean getItemShapeFilled(int series, int item) {
 574:         Boolean flag = this.shapesFilled;
 575:         if (flag == null) {
 576:             flag = getSeriesShapesFilled(series);
 577:         }
 578:         if (flag != null) {
 579:             return flag.booleanValue();   
 580:         }
 581:         else {
 582:             return this.baseShapesFilled;   
 583:         }
 584:     }
 585:     
 586:     /**
 587:      * Sets the 'shapes filled' for ALL series and sends a 
 588:      * {@link RendererChangeEvent} to all registered listeners.
 589:      *
 590:      * @param filled  the flag.
 591:      * 
 592:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 593:      */
 594:     public void setShapesFilled(boolean filled) {
 595:         setShapesFilled(BooleanUtilities.valueOf(filled));
 596:     }
 597: 
 598:     /**
 599:      * Sets the 'shapes filled' for ALL series and sends a 
 600:      * {@link RendererChangeEvent} to all registered listeners.
 601:      *
 602:      * @param filled  the flag (<code>null</code> permitted).
 603:      * 
 604:      * @deprecated As of 1.0.7, use the per-series and base level settings.
 605:      */
 606:     public void setShapesFilled(Boolean filled) {
 607:         this.shapesFilled = filled;
 608:         fireChangeEvent();
 609:     }
 610:     
 611:     /**
 612:      * Returns the flag used to control whether or not the shapes for a series
 613:      * are filled.
 614:      *
 615:      * @param series  the series index (zero-based).
 616:      *
 617:      * @return A boolean.
 618:      * 
 619:      * @see #setSeriesShapesFilled(int, Boolean)
 620:      */
 621:     public Boolean getSeriesShapesFilled(int series) {
 622:         return this.seriesShapesFilled.getBoolean(series);
 623:     }
 624: 
 625:     /**
 626:      * Sets the 'shapes filled' flag for a series and sends a 
 627:      * {@link RendererChangeEvent} to all registered listeners.
 628:      *
 629:      * @param series  the series index (zero-based).
 630:      * @param flag  the flag.
 631:      * 
 632:      * @see #getSeriesShapesFilled(int)
 633:      */
 634:     public void setSeriesShapesFilled(int series, boolean flag) {
 635:         setSeriesShapesFilled(series, BooleanUtilities.valueOf(flag));
 636:     }
 637: 
 638:     /**
 639:      * Sets the 'shapes filled' flag for a series and sends a 
 640:      * {@link RendererChangeEvent} to all registered listeners.
 641:      *
 642:      * @param series  the series index (zero-based).
 643:      * @param flag  the flag.
 644:      * 
 645:      * @see #getSeriesShapesFilled(int)
 646:      */
 647:     public void setSeriesShapesFilled(int series, Boolean flag) {
 648:         this.seriesShapesFilled.setBoolean(series, flag);
 649:         fireChangeEvent();
 650:     }
 651: 
 652:     /**
 653:      * Returns the base 'shape filled' attribute.
 654:      *
 655:      * @return The base flag.
 656:      * 
 657:      * @see #setBaseShapesFilled(boolean)
 658:      */
 659:     public boolean getBaseShapesFilled() {
 660:         return this.baseShapesFilled;
 661:     }
 662: 
 663:     /**
 664:      * Sets the base 'shapes filled' flag and sends a 
 665:      * {@link RendererChangeEvent} to all registered listeners.
 666:      *
 667:      * @param flag  the flag.
 668:      * 
 669:      * @see #getBaseShapesFilled()
 670:      */
 671:     public void setBaseShapesFilled(boolean flag) {
 672:         this.baseShapesFilled = flag;
 673:         fireChangeEvent();
 674:     }
 675: 
 676:     /**
 677:      * Returns <code>true</code> if outlines should be drawn for shapes, and 
 678:      * <code>false</code> otherwise.
 679:      * 
 680:      * @return A boolean.
 681:      * 
 682:      * @see #setDrawOutlines(boolean)
 683:      */
 684:     public boolean getDrawOutlines() {
 685:         return this.drawOutlines;
 686:     }
 687:     
 688:     /**
 689:      * Sets the flag that controls whether outlines are drawn for 
 690:      * shapes, and sends a {@link RendererChangeEvent} to all registered 
 691:      * listeners. 
 692:      * <P>
 693:      * In some cases, shapes look better if they do NOT have an outline, but 
 694:      * this flag allows you to set your own preference.
 695:      * 
 696:      * @param flag  the flag.
 697:      * 
 698:      * @see #getDrawOutlines()
 699:      */
 700:     public void setDrawOutlines(boolean flag) {
 701:         this.drawOutlines = flag;
 702:         fireChangeEvent();
 703:     }
 704:     
 705:     /**
 706:      * Returns <code>true</code> if the renderer should use the fill paint 
 707:      * setting to fill shapes, and <code>false</code> if it should just
 708:      * use the regular paint.
 709:      * <p>
 710:      * Refer to <code>XYLineAndShapeRendererDemo2.java</code> to see the
 711:      * effect of this flag.
 712:      * 
 713:      * @return A boolean.
 714:      * 
 715:      * @see #setUseFillPaint(boolean)
 716:      * @see #getUseOutlinePaint()
 717:      */
 718:     public boolean getUseFillPaint() {
 719:         return this.useFillPaint;
 720:     }
 721:     
 722:     /**
 723:      * Sets the flag that controls whether the fill paint is used to fill 
 724:      * shapes, and sends a {@link RendererChangeEvent} to all 
 725:      * registered listeners.
 726:      * 
 727:      * @param flag  the flag.
 728:      * 
 729:      * @see #getUseFillPaint()
 730:      */
 731:     public void setUseFillPaint(boolean flag) {
 732:         this.useFillPaint = flag;
 733:         fireChangeEvent();
 734:     }
 735:     
 736:     /**
 737:      * Returns <code>true</code> if the renderer should use the outline paint 
 738:      * setting to draw shape outlines, and <code>false</code> if it should just
 739:      * use the regular paint.
 740:      * 
 741:      * @return A boolean.
 742:      * 
 743:      * @see #setUseOutlinePaint(boolean)
 744:      * @see #getUseFillPaint()
 745:      */
 746:     public boolean getUseOutlinePaint() {
 747:         return this.useOutlinePaint;
 748:     }
 749:     
 750:     /**
 751:      * Sets the flag that controls whether the outline paint is used to draw 
 752:      * shape outlines, and sends a {@link RendererChangeEvent} to all 
 753:      * registered listeners.
 754:      * <p>
 755:      * Refer to <code>XYLineAndShapeRendererDemo2.java</code> to see the
 756:      * effect of this flag.
 757:      * 
 758:      * @param flag  the flag.
 759:      * 
 760:      * @see #getUseOutlinePaint()
 761:      */
 762:     public void setUseOutlinePaint(boolean flag) {
 763:         this.useOutlinePaint = flag;
 764:         fireChangeEvent();
 765:     }
 766:     
 767:     /**
 768:      * Records the state for the renderer.  This is used to preserve state 
 769:      * information between calls to the drawItem() method for a single chart 
 770:      * drawing.
 771:      */
 772:     public static class State extends XYItemRendererState {
 773:         
 774:         /** The path for the current series. */
 775:         public GeneralPath seriesPath;
 776:         
 777:         /** 
 778:          * A flag that indicates if the last (x, y) point was 'good' 
 779:          * (non-null). 
 780:          */
 781:         private boolean lastPointGood;
 782:         
 783:         /**
 784:          * Creates a new state instance.
 785:          * 
 786:          * @param info  the plot rendering info.
 787:          */
 788:         public State(PlotRenderingInfo info) {
 789:             super(info);
 790:         }
 791:         
 792:         /**
 793:          * Returns a flag that indicates if the last point drawn (in the 
 794:          * current series) was 'good' (non-null).
 795:          * 
 796:          * @return A boolean.
 797:          */
 798:         public boolean isLastPointGood() {
 799:             return this.lastPointGood;
 800:         }
 801:         
 802:         /**
 803:          * Sets a flag that indicates if the last point drawn (in the current 
 804:          * series) was 'good' (non-null).
 805:          * 
 806:          * @param good  the flag.
 807:          */
 808:         public void setLastPointGood(boolean good) {
 809:             this.lastPointGood = good;
 810:         }
 811:     }
 812:     
 813:     /**
 814:      * Initialises the renderer.
 815:      * <P>
 816:      * This method will be called before the first item is rendered, giving the
 817:      * renderer an opportunity to initialise any state information it wants to 
 818:      * maintain.  The renderer can do nothing if it chooses.
 819:      *
 820:      * @param g2  the graphics device.
 821:      * @param dataArea  the area inside the axes.
 822:      * @param plot  the plot.
 823:      * @param data  the data.
 824:      * @param info  an optional info collection object to return data back to 
 825:      *              the caller.
 826:      *
 827:      * @return The renderer state.
 828:      */
 829:     public XYItemRendererState initialise(Graphics2D g2,
 830:                                           Rectangle2D dataArea,
 831:                                           XYPlot plot,
 832:                                           XYDataset data,
 833:                                           PlotRenderingInfo info) {
 834: 
 835:         State state = new State(info);
 836:         state.seriesPath = new GeneralPath();
 837:         return state;
 838: 
 839:     }
 840:     
 841:     /**
 842:      * Draws the visual representation of a single data item.
 843:      *
 844:      * @param g2  the graphics device.
 845:      * @param state  the renderer state.
 846:      * @param dataArea  the area within which the data is being drawn.
 847:      * @param info  collects information about the drawing.
 848:      * @param plot  the plot (can be used to obtain standard color 
 849:      *              information etc).
 850:      * @param domainAxis  the domain axis.
 851:      * @param rangeAxis  the range axis.
 852:      * @param dataset  the dataset.
 853:      * @param series  the series index (zero-based).
 854:      * @param item  the item index (zero-based).
 855:      * @param crosshairState  crosshair information for the plot 
 856:      *                        (<code>null</code> permitted).
 857:      * @param pass  the pass index.
 858:      */
 859:     public void drawItem(Graphics2D g2,
 860:                          XYItemRendererState state,
 861:                          Rectangle2D dataArea,
 862:                          PlotRenderingInfo info,
 863:                          XYPlot plot,
 864:                          ValueAxis domainAxis,
 865:                          ValueAxis rangeAxis,
 866:                          XYDataset dataset,
 867:                          int series,
 868:                          int item,
 869:                          CrosshairState crosshairState,
 870:                          int pass) {
 871: 
 872:         // do nothing if item is not visible
 873:         if (!getItemVisible(series, item)) {
 874:             return;   
 875:         }
 876: 
 877:         // first pass draws the background (lines, for instance)
 878:         if (isLinePass(pass)) {
 879:             if (item == 0) {
 880:                 if (this.drawSeriesLineAsPath) {
 881:                     State s = (State) state;
 882:                     s.seriesPath.reset();
 883:                     s.lastPointGood = false;     
 884:                 }
 885:             }
 886: 
 887:             if (getItemLineVisible(series, item)) {
 888:                 if (this.drawSeriesLineAsPath) {
 889:                     drawPrimaryLineAsPath(state, g2, plot, dataset, pass, 
 890:                             series, item, domainAxis, rangeAxis, dataArea);
 891:                 }
 892:                 else {
 893:                     drawPrimaryLine(state, g2, plot, dataset, pass, series, 
 894:                             item, domainAxis, rangeAxis, dataArea);
 895:                 }
 896:             }
 897:         }
 898:         // second pass adds shapes where the items are ..
 899:         else if (isItemPass(pass)) {
 900: 
 901:             // setup for collecting optional entity info...
 902:             EntityCollection entities = null;
 903:             if (info != null) {
 904:                 entities = info.getOwner().getEntityCollection();
 905:             }
 906: 
 907:             drawSecondaryPass(g2, plot, dataset, pass, series, item, 
 908:                     domainAxis, dataArea, rangeAxis, crosshairState, entities);
 909:         }
 910:     }
 911: 
 912:     /**
 913:      * Returns <code>true</code> if the specified pass is the one for drawing 
 914:      * lines.
 915:      * 
 916:      * @param pass  the pass.
 917:      * 
 918:      * @return A boolean.
 919:      */
 920:     protected boolean isLinePass(int pass) {
 921:         return pass == 0;
 922:     }
 923: 
 924:     /**
 925:      * Returns <code>true</code> if the specified pass is the one for drawing 
 926:      * items.
 927:      * 
 928:      * @param pass  the pass.
 929:      * 
 930:      * @return A boolean.
 931:      */
 932:     protected boolean isItemPass(int pass) {
 933:         return pass == 1;
 934:     }
 935: 
 936:     /**
 937:      * Draws the item (first pass). This method draws the lines
 938:      * connecting the items.
 939:      *
 940:      * @param g2  the graphics device.
 941:      * @param state  the renderer state.
 942:      * @param dataArea  the area within which the data is being drawn.
 943:      * @param plot  the plot (can be used to obtain standard color 
 944:      *              information etc).
 945:      * @param domainAxis  the domain axis.
 946:      * @param rangeAxis  the range axis.
 947:      * @param dataset  the dataset.
 948:      * @param pass  the pass.
 949:      * @param series  the series index (zero-based).
 950:      * @param item  the item index (zero-based).
 951:      */
 952:     protected void drawPrimaryLine(XYItemRendererState state,
 953:                                    Graphics2D g2,
 954:                                    XYPlot plot,
 955:                                    XYDataset dataset,
 956:                                    int pass,
 957:                                    int series,
 958:                                    int item,
 959:                                    ValueAxis domainAxis,
 960:                                    ValueAxis rangeAxis,
 961:                                    Rectangle2D dataArea) {
 962:         if (item == 0) {
 963:             return;
 964:         }
 965: 
 966:         // get the data point...
 967:         double x1 = dataset.getXValue(series, item);
 968:         double y1 = dataset.getYValue(series, item);
 969:         if (Double.isNaN(y1) || Double.isNaN(x1)) {
 970:             return;
 971:         }
 972: 
 973:         double x0 = dataset.getXValue(series, item - 1);
 974:         double y0 = dataset.getYValue(series, item - 1);
 975:         if (Double.isNaN(y0) || Double.isNaN(x0)) {
 976:             return;
 977:         }
 978: 
 979:         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
 980:         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
 981: 
 982:         double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation);
 983:         double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation);
 984: 
 985:         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
 986:         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
 987: 
 988:         // only draw if we have good values
 989:         if (Double.isNaN(transX0) || Double.isNaN(transY0)
 990:             || Double.isNaN(transX1) || Double.isNaN(transY1)) {
 991:             return;
 992:         }
 993: 
 994:         PlotOrientation orientation = plot.getOrientation();
 995:         if (orientation == PlotOrientation.HORIZONTAL) {
 996:             state.workingLine.setLine(transY0, transX0, transY1, transX1);
 997:         }
 998:         else if (orientation == PlotOrientation.VERTICAL) {
 999:             state.workingLine.setLine(transX0, transY0, transX1, transY1);
1000:         }
1001: 
1002:         if (state.workingLine.intersects(dataArea)) {
1003:             drawFirstPassShape(g2, pass, series, item, state.workingLine);
1004:         }
1005:     }
1006: 
1007:     /**
1008:      * Draws the first pass shape.
1009:      * 
1010:      * @param g2  the graphics device.
1011:      * @param pass  the pass.
1012:      * @param series  the series index.
1013:      * @param item  the item index.
1014:      * @param shape  the shape.
1015:      */
1016:     protected void drawFirstPassShape(Graphics2D g2, int pass, int series,
1017:                                       int item, Shape shape) {
1018:         g2.setStroke(getItemStroke(series, item));
1019:         g2.setPaint(getItemPaint(series, item));
1020:         g2.draw(shape);
1021:     }
1022: 
1023: 
1024:     /**
1025:      * Draws the item (first pass). This method draws the lines
1026:      * connecting the items. Instead of drawing separate lines,
1027:      * a GeneralPath is constructed and drawn at the end of
1028:      * the series painting.
1029:      *
1030:      * @param g2  the graphics device.
1031:      * @param state  the renderer state.
1032:      * @param plot  the plot (can be used to obtain standard color information 
1033:      *              etc).
1034:      * @param dataset  the dataset.
1035:      * @param pass  the pass.
1036:      * @param series  the series index (zero-based).
1037:      * @param item  the item index (zero-based).
1038:      * @param domainAxis  the domain axis.
1039:      * @param rangeAxis  the range axis.
1040:      * @param dataArea  the area within which the data is being drawn.
1041:      */
1042:     protected void drawPrimaryLineAsPath(XYItemRendererState state,
1043:                                          Graphics2D g2, XYPlot plot,
1044:                                          XYDataset dataset,
1045:                                          int pass,
1046:                                          int series,
1047:                                          int item,
1048:                                          ValueAxis domainAxis,
1049:                                          ValueAxis rangeAxis,
1050:                                          Rectangle2D dataArea) {
1051: 
1052: 
1053:         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
1054:         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
1055: 
1056:         // get the data point...
1057:         double x1 = dataset.getXValue(series, item);
1058:         double y1 = dataset.getYValue(series, item);
1059:         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
1060:         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
1061: 
1062:         State s = (State) state;
1063:         // update path to reflect latest point
1064:         if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) {
1065:             float x = (float) transX1;
1066:             float y = (float) transY1;
1067:             PlotOrientation orientation = plot.getOrientation();
1068:             if (orientation == PlotOrientation.HORIZONTAL) {
1069:                 x = (float) transY1;
1070:                 y = (float) transX1;
1071:             }
1072:             if (s.isLastPointGood()) {
1073:                 s.seriesPath.lineTo(x, y);
1074:             }
1075:             else {
1076:                 s.seriesPath.moveTo(x, y);
1077:             }
1078:             s.setLastPointGood(true);
1079:         }
1080:         else {
1081:             s.setLastPointGood(false);
1082:         }
1083:         // if this is the last item, draw the path ...
1084:         if (item == dataset.getItemCount(series) - 1) {
1085:             // draw path
1086:             drawFirstPassShape(g2, pass, series, item, s.seriesPath);
1087:         }
1088:     }
1089: 
1090:     /**
1091:      * Draws the item shapes and adds chart entities (second pass). This method 
1092:      * draws the shapes which mark the item positions. If <code>entities</code> 
1093:      * is not <code>null</code> it will be populated with entity information
1094:      * for points that fall within the data area.
1095:      *
1096:      * @param g2  the graphics device.
1097:      * @param plot  the plot (can be used to obtain standard color 
1098:      *              information etc).
1099:      * @param domainAxis  the domain axis.
1100:      * @param dataArea  the area within which the data is being drawn.
1101:      * @param rangeAxis  the range axis.
1102:      * @param dataset  the dataset.
1103:      * @param pass  the pass.
1104:      * @param series  the series index (zero-based).
1105:      * @param item  the item index (zero-based).
1106:      * @param crosshairState  the crosshair state.
1107:      * @param entities the entity collection.
1108:      */
1109:     protected void drawSecondaryPass(Graphics2D g2, XYPlot plot, 
1110:                                      XYDataset dataset,
1111:                                      int pass, int series, int item,
1112:                                      ValueAxis domainAxis, 
1113:                                      Rectangle2D dataArea,
1114:                                      ValueAxis rangeAxis, 
1115:                                      CrosshairState crosshairState,
1116:                                      EntityCollection entities) {
1117: 
1118:         Shape entityArea = null;
1119:         
1120:         // get the data point...
1121:         double x1 = dataset.getXValue(series, item);
1122:         double y1 = dataset.getYValue(series, item);
1123:         if (Double.isNaN(y1) || Double.isNaN(x1)) {
1124:             return;
1125:         }
1126: 
1127:         PlotOrientation orientation = plot.getOrientation();
1128:         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
1129:         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
1130:         double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
1131:         double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
1132: 
1133:         if (getItemShapeVisible(series, item)) {
1134:             Shape shape = getItemShape(series, item);
1135:             if (orientation == PlotOrientation.HORIZONTAL) {
1136:                 shape = ShapeUtilities.createTranslatedShape(shape, transY1, 
1137:                         transX1);
1138:             }
1139:             else if (orientation == PlotOrientation.VERTICAL) {
1140:                 shape = ShapeUtilities.createTranslatedShape(shape, transX1, 
1141:                         transY1);
1142:             }
1143:             entityArea = shape;
1144:             if (shape.intersects(dataArea)) {
1145:                 if (getItemShapeFilled(series, item)) {
1146:                     if (this.useFillPaint) {
1147:                         g2.setPaint(getItemFillPaint(series, item));
1148:                     }
1149:                     else {
1150:                         g2.setPaint(getItemPaint(series, item));
1151:                     }
1152:                     g2.fill(shape);
1153:                 }
1154:                 if (this.drawOutlines) {
1155:                     if (getUseOutlinePaint()) {
1156:                         g2.setPaint(getItemOutlinePaint(series, item));
1157:                     }
1158:                     else {
1159:                         g2.setPaint(getItemPaint(series, item));
1160:                     }
1161:                     g2.setStroke(getItemOutlineStroke(series, item));
1162:                     g2.draw(shape);
1163:                 }
1164:             }
1165:         }
1166: 
1167:         double xx = transX1;
1168:         double yy = transY1;
1169:         if (orientation == PlotOrientation.HORIZONTAL) {
1170:             xx = transY1;
1171:             yy = transX1;
1172:         }          
1173: 
1174:         // draw the item label if there is one...
1175:         if (isItemLabelVisible(series, item)) {
1176:             drawItemLabel(g2, orientation, dataset, series, item, xx, yy, 
1177:                     (y1 < 0.0));
1178:         }
1179: 
1180:         int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
1181:         int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
1182:         updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 
1183:                 rangeAxisIndex, transX1, transY1, plot.getOrientation());
1184: 
1185:         // add an entity for the item, but only if it falls within the data
1186:         // area...
1187:         if (entities != null && dataArea.contains(xx, yy)) {
1188:             addEntity(entities, entityArea, dataset, series, item, xx, yy);
1189:         }
1190:     }
1191: 
1192: 
1193:     /**
1194:      * Returns a legend item for the specified series.
1195:      *
1196:      * @param datasetIndex  the dataset index (zero-based).
1197:      * @param series  the series index (zero-based).
1198:      *
1199:      * @return A legend item for the series.
1200:      */
1201:     public LegendItem getLegendItem(int datasetIndex, int series) {
1202: 
1203:         XYPlot plot = getPlot();
1204:         if (plot == null) {
1205:             return null;
1206:         }
1207: 
1208:         LegendItem result = null;
1209:         XYDataset dataset = plot.getDataset(datasetIndex);
1210:         if (dataset != null) {
1211:             if (getItemVisible(series, 0)) {
1212:                 String label = getLegendItemLabelGenerator().generateLabel(
1213:                         dataset, series);
1214:                 String description = label;
1215:                 String toolTipText = null;
1216:                 if (getLegendItemToolTipGenerator() != null) {
1217:                     toolTipText = getLegendItemToolTipGenerator().generateLabel(
1218:                             dataset, series);
1219:                 }
1220:                 String urlText = null;
1221:                 if (getLegendItemURLGenerator() != null) {
1222:                     urlText = getLegendItemURLGenerator().generateLabel(
1223:                             dataset, series);
1224:                 }
1225:                 boolean shapeIsVisible = getItemShapeVisible(series, 0);
1226:                 Shape shape = lookupSeriesShape(series);
1227:                 boolean shapeIsFilled = getItemShapeFilled(series, 0);
1228:                 Paint fillPaint = (this.useFillPaint 
1229:                     ? lookupSeriesFillPaint(series) 
1230:                     : lookupSeriesPaint(series));
1231:                 boolean shapeOutlineVisible = this.drawOutlines;  
1232:                 Paint outlinePaint = (this.useOutlinePaint 
1233:                     ? lookupSeriesOutlinePaint(series) 
1234:                     : lookupSeriesPaint(series));
1235:                 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
1236:                 boolean lineVisible = getItemLineVisible(series, 0);
1237:                 Stroke lineStroke = lookupSeriesStroke(series);
1238:                 Paint linePaint = lookupSeriesPaint(series);
1239:                 result = new LegendItem(label, description, toolTipText, 
1240:                         urlText, shapeIsVisible, shape, shapeIsFilled, 
1241:                         fillPaint, shapeOutlineVisible, outlinePaint, 
1242:                         outlineStroke, lineVisible, this.legendLine, 
1243:                         lineStroke, linePaint);
1244:                 result.setSeriesKey(dataset.getSeriesKey(series));
1245:                 result.setSeriesIndex(series);
1246:                 result.setDataset(dataset);
1247:                 result.setDatasetIndex(datasetIndex);
1248:             }
1249:         }
1250: 
1251:         return result;
1252: 
1253:     }
1254:     
1255:     /**
1256:      * Returns a clone of the renderer.
1257:      * 
1258:      * @return A clone.
1259:      * 
1260:      * @throws CloneNotSupportedException if the clone cannot be created.
1261:      */
1262:     public Object clone() throws CloneNotSupportedException {
1263:         XYLineAndShapeRenderer clone = (XYLineAndShapeRenderer) super.clone();
1264:         clone.seriesLinesVisible 
1265:                 = (BooleanList) this.seriesLinesVisible.clone();
1266:         if (this.legendLine != null) {
1267:             clone.legendLine = ShapeUtilities.clone(this.legendLine);
1268:         }
1269:         clone.seriesShapesVisible 
1270:                 = (BooleanList) this.seriesShapesVisible.clone();
1271:         clone.seriesShapesFilled 
1272:                 = (BooleanList) this.seriesShapesFilled.clone();
1273:         return clone;
1274:     }
1275:     
1276:     /**
1277:      * Tests this renderer for equality with an arbitrary object.
1278:      *
1279:      * @param obj  the object (<code>null</code> permitted).
1280:      *
1281:      * @return <code>true</code> or <code>false</code>.
1282:      */
1283:     public boolean equals(Object obj) {
1284: 
1285:         if (obj == this) {
1286:             return true;
1287:         }
1288:         if (!(obj instanceof XYLineAndShapeRenderer)) {
1289:             return false;
1290:         }
1291:         if (!super.equals(obj)) {
1292:             return false;
1293:         }
1294:         XYLineAndShapeRenderer that = (XYLineAndShapeRenderer) obj;
1295:         if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
1296:             return false;
1297:         }
1298:         if (!ObjectUtilities.equal(
1299:             this.seriesLinesVisible, that.seriesLinesVisible)
1300:         ) {
1301:             return false;
1302:         }
1303:         if (this.baseLinesVisible != that.baseLinesVisible) {
1304:             return false;
1305:         }
1306:         if (!ShapeUtilities.equal(this.legendLine, that.legendLine)) {
1307:             return false;   
1308:         }
1309:         if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
1310:             return false;
1311:         }
1312:         if (!ObjectUtilities.equal(
1313:             this.seriesShapesVisible, that.seriesShapesVisible)
1314:         ) {
1315:             return false;
1316:         }
1317:         if (this.baseShapesVisible != that.baseShapesVisible) {
1318:             return false;
1319:         }
1320:         if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
1321:             return false;
1322:         }
1323:         if (!ObjectUtilities.equal(
1324:             this.seriesShapesFilled, that.seriesShapesFilled)
1325:         ) {
1326:             return false;
1327:         }
1328:         if (this.baseShapesFilled != that.baseShapesFilled) {
1329:             return false;
1330:         }
1331:         if (this.drawOutlines != that.drawOutlines) {
1332:             return false;
1333:         }
1334:         if (this.useOutlinePaint != that.useOutlinePaint) {
1335:             return false;
1336:         }
1337:         if (this.useFillPaint != that.useFillPaint) {
1338:             return false;
1339:         }
1340:         if (this.drawSeriesLineAsPath != that.drawSeriesLineAsPath) {
1341:             return false;
1342:         }
1343:         return true;
1344: 
1345:     }
1346:     
1347:     /**
1348:      * Provides serialization support.
1349:      *
1350:      * @param stream  the input stream.
1351:      *
1352:      * @throws IOException  if there is an I/O error.
1353:      * @throws ClassNotFoundException  if there is a classpath problem.
1354:      */
1355:     private void readObject(ObjectInputStream stream) 
1356:             throws IOException, ClassNotFoundException {
1357:         stream.defaultReadObject();
1358:         this.legendLine = SerialUtilities.readShape(stream);
1359:     }
1360:     
1361:     /**
1362:      * Provides serialization support.
1363:      *
1364:      * @param stream  the output stream.
1365:      *
1366:      * @throws IOException  if there is an I/O error.
1367:      */
1368:     private void writeObject(ObjectOutputStream stream) throws IOException {
1369:         stream.defaultWriteObject();
1370:         SerialUtilities.writeShape(this.legendLine, stream);
1371:     }
1372:   
1373: }