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