Source for org.jfree.chart.plot.XYPlot

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * -----------
  28:  * XYPlot.java
  29:  * -----------
  30:  * (C) Copyright 2000-2006, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Craig MacFarlane;
  34:  *                   Mark Watson (www.markwatson.com);
  35:  *                   Jonathan Nash;
  36:  *                   Gideon Krause;
  37:  *                   Klaus Rheinwald;
  38:  *                   Xavier Poinsard;
  39:  *                   Richard Atkinson;
  40:  *                   Arnaud Lelievre;
  41:  *                   Nicolas Brodu;
  42:  *                   Eduardo Ramalho;
  43:  *
  44:  * $Id: XYPlot.java,v 1.44.2.6 2006/08/02 14:27:36 mungady Exp $
  45:  *
  46:  * Changes (from 21-Jun-2001)
  47:  * --------------------------
  48:  * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
  49:  * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
  50:  * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
  51:  * 19-Oct-2001 : Removed the code for drawing the visual representation of each
  52:  *               data point into a separate class StandardXYItemRenderer.
  53:  *               This will make it easier to add variations to the way the
  54:  *               charts are drawn.  Based on code contributed by Mark
  55:  *               Watson (DG);
  56:  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
  57:  * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
  58:  *               inside JScrollPane (DG);
  59:  * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
  60:  * 13-Dec-2001 : Added skeleton code for tooltips.  Added new constructor. (DG);
  61:  * 16-Jan-2002 : Renamed the tooltips class (DG);
  62:  * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
  63:  *               Crosshairs based on code by Jonathan Nash (DG);
  64:  * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
  65:  *               Vieujot (DG);
  66:  * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
  67:  *               special case when chart is null (DG);
  68:  * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
  69:  * 28-Mar-2002 : The plot now registers with the renderer as a property change
  70:  *               listener.  Also added a new constructor (DG);
  71:  * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
  72:  *               method.  Moved the tooltip generator into the renderer (DG);
  73:  * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
  74:  *               lines (DG);
  75:  * 13-May-2002 : Small change to the draw() method so that it works for
  76:  *               OverlaidXYPlot also (DG);
  77:  * 25-Jun-2002 : Removed redundant import (DG);
  78:  * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
  79:  *               setXYItemRenderer() --> setRenderer() (DG);
  80:  * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
  81:  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
  82:  * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
  83:  *               these were set in the axes) (DG);
  84:  * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
  85:  *               border bug fix contributed by Gideon Krause (DG);
  86:  * 22-Jan-2003 : Removed monolithic constructor (DG);
  87:  * 04-Mar-2003 : Added 'no data' message, see bug report 691634.  Added
  88:  *               secondary range markers using code contributed by Klaus
  89:  *               Rheinwald (DG);
  90:  * 26-Mar-2003 : Implemented Serializable (DG);
  91:  * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
  92:  * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
  93:  * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
  94:  * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
  95:  * 15-May-2003 : Added an orientation attribute (DG);
  96:  * 02-Jun-2003 : Removed range axis compatibility test (DG);
  97:  * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
  98:  *               Services Ltd) (DG);
  99:  * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
 100:  * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
 101:  *               overlaid plots) (DG);
 102:  * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
 103:  *               renderers (DG);
 104:  * 27-Jul-2003 : Added support for stacked XY area charts (RA);
 105:  * 19-Aug-2003 : Implemented Cloneable (DG);
 106:  * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
 107:  *               change event (797466) (DG)
 108:  * 08-Sep-2003 : Added internationalization via use of properties
 109:  *               resourceBundle (RFE 690236) (AL);
 110:  * 08-Sep-2003 : Changed ValueAxis API (DG);
 111:  * 08-Sep-2003 : Fixes for serialization (NB);
 112:  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
 113:  * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
 114:  * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
 115:  *               getSecondaryRangeAxisCount() methods suggested by Eduardo
 116:  *               Ramalho (RFE 808548) (DG);
 117:  * 23-Sep-2003 : Split domain and range markers into foreground and
 118:  *               background (DG);
 119:  * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
 120:  *               methods.  Fixed bug (815876) in addSecondaryRangeMarker()
 121:  *               method.  Added new addSecondaryDomainMarker methods (see bug
 122:  *               id 815869) (DG);
 123:  * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
 124:  *               requested by Eduardo Ramalho (DG);
 125:  * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
 126:  *               values (DG);
 127:  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
 128:  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
 129:  * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
 130:  *               range type (DG);
 131:  * 22-Mar-2004 : Fixed cloning bug (DG);
 132:  * 23-Mar-2004 : Fixed more cloning bugs (DG);
 133:  * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
 134:  *               stacked, see this post in the forum:
 135:  *               http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
 136:  * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
 137:  * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
 138:  *               plot (DG);
 139:  * 27-Apr-2004 : Removed major distinction between primary and secondary
 140:  *               datasets, renderers and axes (DG);
 141:  * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
 142:  *               renderer interface (DG);
 143:  * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
 144:  * 19-May-2004 : Added indexOf() method (DG);
 145:  * 03-Jun-2004 : Fixed zooming bug (DG);
 146:  * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
 147:  * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
 148:  * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
 149:  *               the x-value range (now matches behaviour for y-values).  Added
 150:  *               getDomainAxisIndex() method (DG);
 151:  * 12-Nov-2004 : Implemented new Zoomable interface (DG);
 152:  * 25-Nov-2004 : Small update to clone() implementation (DG);
 153:  * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
 154:  * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
 155:  * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
 156:  * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
 157:  * 26-Apr-2005 : Removed LOGGER (DG);
 158:  * 04-May-2005 : Fixed serialization of domain and range markers (DG);
 159:  * 05-May-2005 : Removed unused draw() method (DG);
 160:  * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
 161:  *               RFE 1183100 (DG);
 162:  * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
 163:  *               axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
 164:  * 01-Jun-2005 : Added clearDomainMarkers(int) method to match 
 165:  *               clearRangeMarkers(int) (DG);
 166:  * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
 167:  * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
 168:  * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG);
 169:  * ------------- JFREECHART 1.0.0 ---------------------------------------------
 170:  * 26-Jan-2006 : Added getAnnotations() method (DG);
 171:  *
 172:  */
 173: 
 174: package org.jfree.chart.plot;
 175: 
 176: import java.awt.AlphaComposite;
 177: import java.awt.BasicStroke;
 178: import java.awt.Color;
 179: import java.awt.Composite;
 180: import java.awt.Graphics2D;
 181: import java.awt.Paint;
 182: import java.awt.Shape;
 183: import java.awt.Stroke;
 184: import java.awt.geom.Line2D;
 185: import java.awt.geom.Point2D;
 186: import java.awt.geom.Rectangle2D;
 187: import java.io.IOException;
 188: import java.io.ObjectInputStream;
 189: import java.io.ObjectOutputStream;
 190: import java.io.Serializable;
 191: import java.util.ArrayList;
 192: import java.util.Collection;
 193: import java.util.Collections;
 194: import java.util.HashMap;
 195: import java.util.Iterator;
 196: import java.util.List;
 197: import java.util.Map;
 198: import java.util.ResourceBundle;
 199: import java.util.TreeMap;
 200: 
 201: import org.jfree.chart.LegendItem;
 202: import org.jfree.chart.LegendItemCollection;
 203: import org.jfree.chart.annotations.XYAnnotation;
 204: import org.jfree.chart.axis.Axis;
 205: import org.jfree.chart.axis.AxisCollection;
 206: import org.jfree.chart.axis.AxisLocation;
 207: import org.jfree.chart.axis.AxisSpace;
 208: import org.jfree.chart.axis.AxisState;
 209: import org.jfree.chart.axis.ValueAxis;
 210: import org.jfree.chart.axis.ValueTick;
 211: import org.jfree.chart.event.ChartChangeEventType;
 212: import org.jfree.chart.event.PlotChangeEvent;
 213: import org.jfree.chart.event.RendererChangeEvent;
 214: import org.jfree.chart.event.RendererChangeListener;
 215: import org.jfree.chart.renderer.xy.XYItemRenderer;
 216: import org.jfree.chart.renderer.xy.XYItemRendererState;
 217: import org.jfree.data.Range;
 218: import org.jfree.data.general.Dataset;
 219: import org.jfree.data.general.DatasetChangeEvent;
 220: import org.jfree.data.general.DatasetUtilities;
 221: import org.jfree.data.xy.XYDataset;
 222: import org.jfree.io.SerialUtilities;
 223: import org.jfree.ui.Layer;
 224: import org.jfree.ui.RectangleEdge;
 225: import org.jfree.ui.RectangleInsets;
 226: import org.jfree.util.ObjectList;
 227: import org.jfree.util.ObjectUtilities;
 228: import org.jfree.util.PaintUtilities;
 229: import org.jfree.util.PublicCloneable;
 230: 
 231: /**
 232:  * A general class for plotting data in the form of (x, y) pairs.  This plot can
 233:  * use data from any class that implements the {@link XYDataset} interface.
 234:  * <P>
 235:  * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
 236:  * on the plot.  By using different renderers, various chart types can be
 237:  * produced.
 238:  * <p>
 239:  * The {@link org.jfree.chart.ChartFactory} class contains static methods for
 240:  * creating pre-configured charts.
 241:  */
 242: public class XYPlot extends Plot implements ValueAxisPlot,
 243:                                             Zoomable,
 244:                                             RendererChangeListener,
 245:                                             Cloneable, PublicCloneable,
 246:                                             Serializable {
 247: 
 248:     /** For serialization. */
 249:     private static final long serialVersionUID = 7044148245716569264L;
 250:     
 251:     /** The default grid line stroke. */
 252:     public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(
 253:         0.5f,
 254:         BasicStroke.CAP_BUTT,
 255:         BasicStroke.JOIN_BEVEL,
 256:         0.0f,
 257:         new float[] {2.0f, 2.0f},
 258:         0.0f
 259:     );
 260: 
 261:     /** The default grid line paint. */
 262:     public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
 263: 
 264:     /** The default crosshair visibility. */
 265:     public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
 266: 
 267:     /** The default crosshair stroke. */
 268:     public static final Stroke DEFAULT_CROSSHAIR_STROKE
 269:         = DEFAULT_GRIDLINE_STROKE;
 270: 
 271:     /** The default crosshair paint. */
 272:     public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
 273: 
 274:     /** The resourceBundle for the localization. */
 275:     protected static ResourceBundle localizationResources
 276:         = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
 277: 
 278:     /** The plot orientation. */
 279:     private PlotOrientation orientation;
 280: 
 281:     /** The offset between the data area and the axes. */
 282:     private RectangleInsets axisOffset;
 283: 
 284:     /** The domain axis / axes (used for the x-values). */
 285:     private ObjectList domainAxes;
 286: 
 287:     /** The domain axis locations. */
 288:     private ObjectList domainAxisLocations;
 289: 
 290:     /** The range axis (used for the y-values). */
 291:     private ObjectList rangeAxes;
 292: 
 293:     /** The range axis location. */
 294:     private ObjectList rangeAxisLocations;
 295: 
 296:     /** Storage for the datasets. */
 297:     private ObjectList datasets;
 298: 
 299:     /** Storage for the renderers. */
 300:     private ObjectList renderers;
 301: 
 302:     /**
 303:      * Storage for keys that map datasets/renderers to domain axes.  If the
 304:      * map contains no entry for a dataset, it is assumed to map to the
 305:      * primary domain axis (index = 0).
 306:      */
 307:     private Map datasetToDomainAxisMap;
 308: 
 309:     /**
 310:      * Storage for keys that map datasets/renderers to range axes. If the
 311:      * map contains no entry for a dataset, it is assumed to map to the
 312:      * primary domain axis (index = 0).
 313:      */
 314:     private Map datasetToRangeAxisMap;
 315: 
 316:     /** The origin point for the quadrants (if drawn). */
 317:     private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0);
 318: 
 319:     /** The paint used for each quadrant. */
 320:     private transient Paint[] quadrantPaint
 321:         = new Paint[] {null, null, null, null};
 322: 
 323:     /** A flag that controls whether the domain grid-lines are visible. */
 324:     private boolean domainGridlinesVisible;
 325: 
 326:     /** The stroke used to draw the domain grid-lines. */
 327:     private transient Stroke domainGridlineStroke;
 328: 
 329:     /** The paint used to draw the domain grid-lines. */
 330:     private transient Paint domainGridlinePaint;
 331: 
 332:     /** A flag that controls whether the range grid-lines are visible. */
 333:     private boolean rangeGridlinesVisible;
 334: 
 335:     /** The stroke used to draw the range grid-lines. */
 336:     private transient Stroke rangeGridlineStroke;
 337: 
 338:     /** The paint used to draw the range grid-lines. */
 339:     private transient Paint rangeGridlinePaint;
 340: 
 341:     /** 
 342:      * A flag that controls whether or not the zero baseline against the range
 343:      * axis is visible.
 344:      */
 345:     private boolean rangeZeroBaselineVisible;
 346: 
 347:     /** The stroke used for the zero baseline against the range axis. */
 348:     private transient Stroke rangeZeroBaselineStroke;
 349: 
 350:     /** The paint used for the zero baseline against the range axis. */
 351:     private transient Paint rangeZeroBaselinePaint;
 352: 
 353:     /** A flag that controls whether or not a domain crosshair is drawn..*/
 354:     private boolean domainCrosshairVisible;
 355: 
 356:     /** The domain crosshair value. */
 357:     private double domainCrosshairValue;
 358: 
 359:     /** The pen/brush used to draw the crosshair (if any). */
 360:     private transient Stroke domainCrosshairStroke;
 361: 
 362:     /** The color used to draw the crosshair (if any). */
 363:     private transient Paint domainCrosshairPaint;
 364: 
 365:     /**
 366:      * A flag that controls whether or not the crosshair locks onto actual
 367:      * data points.
 368:      */
 369:     private boolean domainCrosshairLockedOnData = true;
 370: 
 371:     /** A flag that controls whether or not a range crosshair is drawn..*/
 372:     private boolean rangeCrosshairVisible;
 373: 
 374:     /** The range crosshair value. */
 375:     private double rangeCrosshairValue;
 376: 
 377:     /** The pen/brush used to draw the crosshair (if any). */
 378:     private transient Stroke rangeCrosshairStroke;
 379: 
 380:     /** The color used to draw the crosshair (if any). */
 381:     private transient Paint rangeCrosshairPaint;
 382: 
 383:     /**
 384:      * A flag that controls whether or not the crosshair locks onto actual
 385:      * data points.
 386:      */
 387:     private boolean rangeCrosshairLockedOnData = true;
 388: 
 389:     /** A map of lists of foreground markers (optional) for the domain axes. */
 390:     private Map foregroundDomainMarkers;
 391: 
 392:     /** A map of lists of background markers (optional) for the domain axes. */
 393:     private Map backgroundDomainMarkers;
 394: 
 395:     /** A map of lists of foreground markers (optional) for the range axes. */
 396:     private Map foregroundRangeMarkers;
 397: 
 398:     /** A map of lists of background markers (optional) for the range axes. */
 399:     private Map backgroundRangeMarkers;
 400: 
 401:     /** 
 402:      * A (possibly empty) list of annotations for the plot.  The list should
 403:      * be initialised in the constructor and never allowed to be 
 404:      * <code>null</code>.
 405:      */
 406:     private List annotations;
 407: 
 408:     /** The paint used for the domain tick bands (if any). */
 409:     private transient Paint domainTickBandPaint;
 410: 
 411:     /** The paint used for the range tick bands (if any). */
 412:     private transient Paint rangeTickBandPaint;
 413: 
 414:     /** The fixed domain axis space. */
 415:     private AxisSpace fixedDomainAxisSpace;
 416: 
 417:     /** The fixed range axis space. */
 418:     private AxisSpace fixedRangeAxisSpace;
 419: 
 420:     /**
 421:      * The order of the dataset rendering (REVERSE draws the primary dataset
 422:      * last so that it appears to be on top).
 423:      */
 424:     private DatasetRenderingOrder datasetRenderingOrder
 425:         = DatasetRenderingOrder.REVERSE;
 426: 
 427:     /**
 428:      * The order of the series rendering (REVERSE draws the primary series
 429:      * last so that it appears to be on top).
 430:      */
 431:     private SeriesRenderingOrder seriesRenderingOrder
 432:         = SeriesRenderingOrder.REVERSE;
 433: 
 434:     /**
 435:      * The weight for this plot (only relevant if this is a subplot in a
 436:      * combined plot).
 437:      */
 438:     private int weight;
 439: 
 440:     /**
 441:      * An optional collection of legend items that can be returned by the
 442:      * getLegendItems() method.
 443:      */
 444:     private LegendItemCollection fixedLegendItems;
 445: 
 446:     /**
 447:      * Default constructor.
 448:      */
 449:     public XYPlot() {
 450:         this(null, null, null, null);
 451:     }
 452: 
 453:     /**
 454:      * Creates a new plot.
 455:      *
 456:      * @param dataset  the dataset (<code>null</code> permitted).
 457:      * @param domainAxis  the domain axis (<code>null</code> permitted).
 458:      * @param rangeAxis  the range axis (<code>null</code> permitted).
 459:      * @param renderer  the renderer (<code>null</code> permitted).
 460:      */
 461:     public XYPlot(XYDataset dataset,
 462:                   ValueAxis domainAxis,
 463:                   ValueAxis rangeAxis,
 464:                   XYItemRenderer renderer) {
 465: 
 466:         super();
 467: 
 468:         this.orientation = PlotOrientation.VERTICAL;
 469:         this.weight = 1;  // only relevant when this is a subplot
 470:         this.axisOffset = RectangleInsets.ZERO_INSETS;
 471: 
 472:         // allocate storage for datasets, axes and renderers (all optional)
 473:         this.domainAxes = new ObjectList();
 474:         this.domainAxisLocations = new ObjectList();
 475:         this.foregroundDomainMarkers = new HashMap();
 476:         this.backgroundDomainMarkers = new HashMap();
 477: 
 478:         this.rangeAxes = new ObjectList();
 479:         this.rangeAxisLocations = new ObjectList();
 480:         this.foregroundRangeMarkers = new HashMap();
 481:         this.backgroundRangeMarkers = new HashMap();
 482: 
 483:         this.datasets = new ObjectList();
 484:         this.renderers = new ObjectList();
 485: 
 486:         this.datasetToDomainAxisMap = new TreeMap();
 487:         this.datasetToRangeAxisMap = new TreeMap();
 488: 
 489:         this.datasets.set(0, dataset);
 490:         if (dataset != null) {
 491:             dataset.addChangeListener(this);
 492:         }
 493: 
 494:         this.renderers.set(0, renderer);
 495:         if (renderer != null) {
 496:             renderer.setPlot(this);
 497:             renderer.addChangeListener(this);
 498:         }
 499: 
 500:         this.domainAxes.set(0, domainAxis);
 501:         this.mapDatasetToDomainAxis(0, 0);
 502:         if (domainAxis != null) {
 503:             domainAxis.setPlot(this);
 504:             domainAxis.addChangeListener(this);
 505:         }
 506:         this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
 507: 
 508:         this.rangeAxes.set(0, rangeAxis);
 509:         this.mapDatasetToRangeAxis(0, 0);
 510:         if (rangeAxis != null) {
 511:             rangeAxis.setPlot(this);
 512:             rangeAxis.addChangeListener(this);
 513:         }
 514:         this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
 515: 
 516:         configureDomainAxes();
 517:         configureRangeAxes();
 518: 
 519:         this.domainGridlinesVisible = true;
 520:         this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
 521:         this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
 522: 
 523:         this.rangeGridlinesVisible = true;
 524:         this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
 525:         this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
 526: 
 527:         this.rangeZeroBaselineVisible = false;
 528:         this.rangeZeroBaselinePaint = Color.black;
 529:         this.rangeZeroBaselineStroke = new BasicStroke(0.5f);
 530: 
 531:         this.domainCrosshairVisible = false;
 532:         this.domainCrosshairValue = 0.0;
 533:         this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
 534:         this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
 535: 
 536:         this.rangeCrosshairVisible = false;
 537:         this.rangeCrosshairValue = 0.0;
 538:         this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
 539:         this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
 540: 
 541:         this.annotations = new java.util.ArrayList();
 542: 
 543:     }
 544: 
 545:     /**
 546:      * Returns the plot type as a string.
 547:      *
 548:      * @return A short string describing the type of plot.
 549:      */
 550:     public String getPlotType() {
 551:         return localizationResources.getString("XY_Plot");
 552:     }
 553: 
 554:     /**
 555:      * Returns the orientation of the plot.
 556:      *
 557:      * @return The orientation of the plot.
 558:      */
 559:     public PlotOrientation getOrientation() {
 560:         return this.orientation;
 561:     }
 562: 
 563:     /**
 564:      * Sets the orientation for the plot.
 565:      *
 566:      * @param orientation  the orientation (<code>null</code> not allowed).
 567:      */
 568:     public void setOrientation(PlotOrientation orientation) {
 569:         if (orientation == null) {
 570:             throw new IllegalArgumentException("Null 'orientation' argument.");
 571:         }
 572:         if (orientation != this.orientation) {
 573:             this.orientation = orientation;
 574:             notifyListeners(new PlotChangeEvent(this));
 575:         }
 576:     }
 577: 
 578:     /**
 579:      * Returns the axis offset.
 580:      *
 581:      * @return The axis offset (never <code>null</code>).
 582:      */
 583:     public RectangleInsets getAxisOffset() {
 584:         return this.axisOffset;
 585:     }
 586: 
 587:     /**
 588:      * Sets the axis offsets (gap between the data area and the axes).
 589:      *
 590:      * @param offset  the offset (<code>null</code> not permitted).
 591:      */
 592:     public void setAxisOffset(RectangleInsets offset) {
 593:         if (offset == null) {
 594:             throw new IllegalArgumentException("Null 'offset' argument.");
 595:         }
 596:         this.axisOffset = offset;
 597:         notifyListeners(new PlotChangeEvent(this));
 598:     }
 599: 
 600:     /**
 601:      * Returns the domain axis for the plot.  If the domain axis for this plot
 602:      * is null, then the method will return the parent plot's domain axis (if
 603:      * there is a parent plot).
 604:      *
 605:      * @return The domain axis.
 606:      */
 607:     public ValueAxis getDomainAxis() {
 608:         return getDomainAxis(0);
 609:     }
 610: 
 611:     /**
 612:      * Returns a domain axis.
 613:      *
 614:      * @param index  the axis index.
 615:      *
 616:      * @return The axis (<code>null</code> possible).
 617:      */
 618:     public ValueAxis getDomainAxis(int index) {
 619:         ValueAxis result = null;
 620:         if (index < this.domainAxes.size()) {
 621:             result = (ValueAxis) this.domainAxes.get(index);
 622:         }
 623:         if (result == null) {
 624:             Plot parent = getParent();
 625:             if (parent instanceof XYPlot) {
 626:                 XYPlot xy = (XYPlot) parent;
 627:                 result = xy.getDomainAxis(index);
 628:             }
 629:         }
 630:         return result;
 631:     }
 632: 
 633:     /**
 634:      * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
 635:      * to all registered listeners.
 636:      *
 637:      * @param axis  the new axis (<code>null</code> permitted).
 638:      */
 639:     public void setDomainAxis(ValueAxis axis) {
 640:         setDomainAxis(0, axis);
 641:     }
 642: 
 643:     /**
 644:      * Sets a domain axis and sends a {@link PlotChangeEvent} to all
 645:      * registered listeners.
 646:      *
 647:      * @param index  the axis index.
 648:      * @param axis  the axis.
 649:      */
 650:     public void setDomainAxis(int index, ValueAxis axis) {
 651:         setDomainAxis(index, axis, true);
 652:     }
 653:     
 654:     /**
 655:      * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
 656:      * all registered listeners.
 657:      *
 658:      * @param index  the axis index.
 659:      * @param axis  the axis.
 660:      * @param notify  notify listeners?
 661:      */
 662:     public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
 663:         ValueAxis existing = getDomainAxis(index);
 664:         if (existing != null) {
 665:             existing.removeChangeListener(this);
 666:         }
 667:         if (axis != null) {
 668:             axis.setPlot(this);
 669:         }
 670:         this.domainAxes.set(index, axis);
 671:         if (axis != null) {
 672:             axis.configure();
 673:             axis.addChangeListener(this);
 674:         }
 675:         if (notify) {
 676:             notifyListeners(new PlotChangeEvent(this));
 677:         }
 678:     }
 679: 
 680:     /**
 681:      * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
 682:      * to all registered listeners.
 683:      * 
 684:      * @param axes  the axes.
 685:      */
 686:     public void setDomainAxes(ValueAxis[] axes) {
 687:         for (int i = 0; i < axes.length; i++) {
 688:             setDomainAxis(i, axes[i], false);   
 689:         }
 690:         notifyListeners(new PlotChangeEvent(this));
 691:     }
 692:     
 693:     /**
 694:      * Returns the location of the primary domain axis.
 695:      *
 696:      * @return The location (never <code>null</code>).
 697:      */
 698:     public AxisLocation getDomainAxisLocation() {
 699:         return (AxisLocation) this.domainAxisLocations.get(0);
 700:     }
 701: 
 702:     /**
 703:      * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
 704:      * to all registered listeners.
 705:      *
 706:      * @param location  the location (<code>null</code> not permitted).
 707:      */
 708:     public void setDomainAxisLocation(AxisLocation location) {
 709:         // defer argument checking...
 710:         setDomainAxisLocation(location, true);
 711:     }
 712: 
 713:     /**
 714:      * Sets the location of the domain axis and, if requested, sends a
 715:      * {@link PlotChangeEvent} to all registered listeners.
 716:      *
 717:      * @param location  the location (<code>null</code> not permitted).
 718:      * @param notify  notify listeners?
 719:      */
 720:     public void setDomainAxisLocation(AxisLocation location, boolean notify) {
 721:         if (location == null) {
 722:             throw new IllegalArgumentException("Null 'location' argument.");
 723:         }
 724:         this.domainAxisLocations.set(0, location);
 725:         if (notify) {
 726:             notifyListeners(new PlotChangeEvent(this));
 727:         }
 728:     }
 729: 
 730:     /**
 731:      * Returns the edge for the primary domain axis (taking into account the
 732:      * plot's orientation.
 733:      *
 734:      * @return The edge.
 735:      */
 736:     public RectangleEdge getDomainAxisEdge() {
 737:         return Plot.resolveDomainAxisLocation(
 738:             getDomainAxisLocation(), this.orientation
 739:         );
 740:     }
 741: 
 742:     /**
 743:      * Returns the number of domain axes.
 744:      *
 745:      * @return The axis count.
 746:      */
 747:     public int getDomainAxisCount() {
 748:         return this.domainAxes.size();
 749:     }
 750: 
 751:     /**
 752:      * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
 753:      * to all registered listeners.
 754:      */
 755:     public void clearDomainAxes() {
 756:         for (int i = 0; i < this.domainAxes.size(); i++) {
 757:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
 758:             if (axis != null) {
 759:                 axis.removeChangeListener(this);
 760:             }
 761:         }
 762:         this.domainAxes.clear();
 763:         notifyListeners(new PlotChangeEvent(this));
 764:     }
 765: 
 766:     /**
 767:      * Configures the domain axes.
 768:      */
 769:     public void configureDomainAxes() {
 770:         for (int i = 0; i < this.domainAxes.size(); i++) {
 771:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
 772:             if (axis != null) {
 773:                 axis.configure();
 774:             }
 775:         }
 776:     }
 777: 
 778:     /**
 779:      * Returns the location for a domain axis.  If this hasn't been set
 780:      * explicitly, the method returns the location that is opposite to the
 781:      * primary domain axis location.
 782:      *
 783:      * @param index  the axis index.
 784:      *
 785:      * @return The location (never <code>null</code>).
 786:      */
 787:     public AxisLocation getDomainAxisLocation(int index) {
 788:         AxisLocation result = null;
 789:         if (index < this.domainAxisLocations.size()) {
 790:             result = (AxisLocation) this.domainAxisLocations.get(index);
 791:         }
 792:         if (result == null) {
 793:             result = AxisLocation.getOpposite(getDomainAxisLocation());
 794:         }
 795:         return result;
 796:     }
 797: 
 798:     /**
 799:      * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
 800:      * to all registered listeners.
 801:      *
 802:      * @param index  the axis index.
 803:      * @param location  the location (<code>null</code> permitted).
 804:      */
 805:     public void setDomainAxisLocation(int index, AxisLocation location) {
 806:         this.domainAxisLocations.set(index, location);
 807:         notifyListeners(new PlotChangeEvent(this));
 808:     }
 809: 
 810:     /**
 811:      * Returns the edge for a domain axis.
 812:      *
 813:      * @param index  the axis index.
 814:      *
 815:      * @return The edge.
 816:      */
 817:     public RectangleEdge getDomainAxisEdge(int index) {
 818:         AxisLocation location = getDomainAxisLocation(index);
 819:         RectangleEdge result = Plot.resolveDomainAxisLocation(
 820:             location, this.orientation
 821:         );
 822:         if (result == null) {
 823:             result = RectangleEdge.opposite(getDomainAxisEdge());
 824:         }
 825:         return result;
 826:     }
 827: 
 828:     /**
 829:      * Returns the range axis for the plot.  If the range axis for this plot is
 830:      * null, then the method will return the parent plot's range axis (if
 831:      * there is a parent plot).
 832:      *
 833:      * @return The range axis.
 834:      */
 835:     public ValueAxis getRangeAxis() {
 836:         return getRangeAxis(0);
 837:     }
 838: 
 839:     /**
 840:      * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
 841:      * all registered listeners.
 842:      *
 843:      * @param axis  the axis (<code>null</code> permitted).
 844:      *
 845:      */
 846:     public void setRangeAxis(ValueAxis axis)  {
 847: 
 848:         if (axis != null) {
 849:             axis.setPlot(this);
 850:         }
 851: 
 852:         // plot is likely registered as a listener with the existing axis...
 853:         ValueAxis existing = getRangeAxis();
 854:         if (existing != null) {
 855:             existing.removeChangeListener(this);
 856:         }
 857: 
 858:         this.rangeAxes.set(0, axis);
 859:         if (axis != null) {
 860:             axis.configure();
 861:             axis.addChangeListener(this);
 862:         }
 863:         notifyListeners(new PlotChangeEvent(this));
 864: 
 865:     }
 866: 
 867:     /**
 868:      * Returns the location of the primary range axis.
 869:      *
 870:      * @return The location (never <code>null</code>).
 871:      */
 872:     public AxisLocation getRangeAxisLocation() {
 873:         return (AxisLocation) this.rangeAxisLocations.get(0);
 874:     }
 875: 
 876:     /**
 877:      * Sets the location of the primary range axis and sends a
 878:      * {@link PlotChangeEvent} to all registered listeners.
 879:      *
 880:      * @param location  the location (<code>null</code> not permitted).
 881:      */
 882:     public void setRangeAxisLocation(AxisLocation location) {
 883:         // defer argument checking...
 884:         setRangeAxisLocation(location, true);
 885:     }
 886: 
 887:     /**
 888:      * Sets the location of the primary range axis and, if requested, sends a
 889:      * {@link PlotChangeEvent} to all registered listeners.
 890:      *
 891:      * @param location  the location (<code>null</code> not permitted).
 892:      * @param notify  notify listeners?
 893:      */
 894:     public void setRangeAxisLocation(AxisLocation location, boolean notify) {
 895:         if (location == null) {
 896:             throw new IllegalArgumentException("Null 'location' argument.");
 897:         }
 898:         this.rangeAxisLocations.set(0, location);
 899:         if (notify) {
 900:             notifyListeners(new PlotChangeEvent(this));
 901:         }
 902: 
 903:     }
 904: 
 905:     /**
 906:      * Returns the edge for the primary range axis.
 907:      *
 908:      * @return The range axis edge.
 909:      */
 910:     public RectangleEdge getRangeAxisEdge() {
 911:         return Plot.resolveRangeAxisLocation(
 912:             getRangeAxisLocation(), this.orientation
 913:         );
 914:     }
 915: 
 916:     /**
 917:      * Returns a range axis.
 918:      *
 919:      * @param index  the axis index.
 920:      *
 921:      * @return The axis (<code>null</code> possible).
 922:      */
 923:     public ValueAxis getRangeAxis(int index) {
 924:         ValueAxis result = null;
 925:         if (index < this.rangeAxes.size()) {
 926:             result = (ValueAxis) this.rangeAxes.get(index);
 927:         }
 928:         if (result == null) {
 929:             Plot parent = getParent();
 930:             if (parent instanceof XYPlot) {
 931:                 XYPlot xy = (XYPlot) parent;
 932:                 result = xy.getRangeAxis(index);
 933:             }
 934:         }
 935:         return result;
 936:     }
 937: 
 938:     /**
 939:      * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
 940:      * listeners.
 941:      *
 942:      * @param index  the axis index.
 943:      * @param axis  the axis (<code>null</code> permitted).
 944:      */
 945:     public void setRangeAxis(int index, ValueAxis axis) {
 946:         setRangeAxis(index, axis, true);
 947:     } 
 948:     
 949:     /**
 950:      * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to 
 951:      * all registered listeners.
 952:      *
 953:      * @param index  the axis index.
 954:      * @param axis  the axis (<code>null</code> permitted).
 955:      * @param notify  notify listeners?
 956:      */
 957:     public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
 958:         ValueAxis existing = getRangeAxis(index);
 959:         if (existing != null) {
 960:             existing.removeChangeListener(this);
 961:         }
 962:         if (axis != null) {
 963:             axis.setPlot(this);
 964:         }
 965:         this.rangeAxes.set(index, axis);
 966:         if (axis != null) {
 967:             axis.configure();
 968:             axis.addChangeListener(this);
 969:         }
 970:         if (notify) {
 971:             notifyListeners(new PlotChangeEvent(this));
 972:         }
 973:     }
 974: 
 975:     /**
 976:      * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
 977:      * to all registered listeners.
 978:      * 
 979:      * @param axes  the axes.
 980:      */
 981:     public void setRangeAxes(ValueAxis[] axes) {
 982:         for (int i = 0; i < axes.length; i++) {
 983:             setRangeAxis(i, axes[i], false);   
 984:         }
 985:         notifyListeners(new PlotChangeEvent(this));
 986:     }
 987:     
 988:     /**
 989:      * Returns the number of range axes.
 990:      *
 991:      * @return The axis count.
 992:      */
 993:     public int getRangeAxisCount() {
 994:         return this.rangeAxes.size();
 995:     }
 996: 
 997:     /**
 998:      * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
 999:      * to all registered listeners.
1000:      */
1001:     public void clearRangeAxes() {
1002:         for (int i = 0; i < this.rangeAxes.size(); i++) {
1003:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1004:             if (axis != null) {
1005:                 axis.removeChangeListener(this);
1006:             }
1007:         }
1008:         this.rangeAxes.clear();
1009:         notifyListeners(new PlotChangeEvent(this));
1010:     }
1011: 
1012:     /**
1013:      * Configures the range axes.
1014:      */
1015:     public void configureRangeAxes() {
1016:         for (int i = 0; i < this.rangeAxes.size(); i++) {
1017:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1018:             if (axis != null) {
1019:                 axis.configure();
1020:             }
1021:         }
1022:     }
1023: 
1024:     /**
1025:      * Returns the location for a range axis.  If this hasn't been set
1026:      * explicitly, the method returns the location that is opposite to the
1027:      * primary range axis location.
1028:      *
1029:      * @param index  the axis index.
1030:      *
1031:      * @return The location (never <code>null</code>).
1032:      */
1033:     public AxisLocation getRangeAxisLocation(int index) {
1034:         AxisLocation result = null;
1035:         if (index < this.rangeAxisLocations.size()) {
1036:             result = (AxisLocation) this.rangeAxisLocations.get(index);
1037:         }
1038:         if (result == null) {
1039:             result = AxisLocation.getOpposite(getRangeAxisLocation());
1040:         }
1041:         return result;
1042:     }
1043: 
1044:     /**
1045:      * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1046:      * to all registered listeners.
1047:      *
1048:      * @param index  the axis index.
1049:      * @param location  the location (<code>null</code> permitted).
1050:      */
1051:     public void setRangeAxisLocation(int index, AxisLocation location) {
1052:         this.rangeAxisLocations.set(index, location);
1053:         notifyListeners(new PlotChangeEvent(this));
1054:     }
1055: 
1056:     /**
1057:      * Returns the edge for a range axis.
1058:      *
1059:      * @param index  the axis index.
1060:      *
1061:      * @return The edge.
1062:      */
1063:     public RectangleEdge getRangeAxisEdge(int index) {
1064:         AxisLocation location = getRangeAxisLocation(index);
1065:         RectangleEdge result = Plot.resolveRangeAxisLocation(
1066:             location, this.orientation
1067:         );
1068:         if (result == null) {
1069:             result = RectangleEdge.opposite(getRangeAxisEdge());
1070:         }
1071:         return result;
1072:     }
1073: 
1074:     /**
1075:      * Returns the primary dataset for the plot.
1076:      *
1077:      * @return The primary dataset (possibly <code>null</code>).
1078:      */
1079:     public XYDataset getDataset() {
1080:         return getDataset(0);
1081:     }
1082: 
1083:     /**
1084:      * Returns a dataset.
1085:      *
1086:      * @param index  the dataset index.
1087:      *
1088:      * @return The dataset (possibly <code>null</code>).
1089:      */
1090:     public XYDataset getDataset(int index) {
1091:         XYDataset result = null;
1092:         if (this.datasets.size() > index) {
1093:             result = (XYDataset) this.datasets.get(index);
1094:         }
1095:         return result;
1096:     }
1097: 
1098:     /**
1099:      * Sets the primary dataset for the plot, replacing the existing dataset if
1100:      * there is one.
1101:      *
1102:      * @param dataset  the dataset (<code>null</code> permitted).
1103:      */
1104:     public void setDataset(XYDataset dataset) {
1105:         setDataset(0, dataset);
1106:     }
1107: 
1108:     /**
1109:      * Sets a dataset for the plot.
1110:      *
1111:      * @param index  the dataset index.
1112:      * @param dataset  the dataset (<code>null</code> permitted).
1113:      */
1114:     public void setDataset(int index, XYDataset dataset) {
1115:         XYDataset existing = getDataset(index);
1116:         if (existing != null) {
1117:             existing.removeChangeListener(this);
1118:         }
1119:         this.datasets.set(index, dataset);
1120:         if (dataset != null) {
1121:             dataset.addChangeListener(this);
1122:         }
1123: 
1124:         // send a dataset change event to self...
1125:         DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1126:         datasetChanged(event);
1127:     }
1128: 
1129:     /**
1130:      * Returns the number of datasets.
1131:      *
1132:      * @return The number of datasets.
1133:      */
1134:     public int getDatasetCount() {
1135:         return this.datasets.size();
1136:     }
1137: 
1138:     /**
1139:      * Returns the index of the specified dataset, or <code>-1</code> if the
1140:      * dataset does not belong to the plot.
1141:      *
1142:      * @param dataset  the dataset (<code>null</code> not permitted).
1143:      *
1144:      * @return The index.
1145:      */
1146:     public int indexOf(XYDataset dataset) {
1147:         int result = -1;
1148:         for (int i = 0; i < this.datasets.size(); i++) {
1149:             if (dataset == this.datasets.get(i)) {
1150:                 result = i;
1151:                 break;
1152:             }
1153:         }
1154:         return result;
1155:     }
1156: 
1157:     /**
1158:      * Maps a dataset to a particular domain axis.  All data will be plotted
1159:      * against axis zero by default, no mapping is required for this case.
1160:      *
1161:      * @param index  the dataset index (zero-based).
1162:      * @param axisIndex  the axis index.
1163:      */
1164:     public void mapDatasetToDomainAxis(int index, int axisIndex) {
1165:         this.datasetToDomainAxisMap.put(
1166:             new Integer(index), new Integer(axisIndex)
1167:         );
1168:         // fake a dataset change event to update axes...
1169:         datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1170:     }
1171: 
1172:     /**
1173:      * Maps a dataset to a particular range axis.  All data will be plotted
1174:      * against axis zero by default, no mapping is required for this case.
1175:      *
1176:      * @param index  the dataset index (zero-based).
1177:      * @param axisIndex  the axis index.
1178:      */
1179:     public void mapDatasetToRangeAxis(int index, int axisIndex) {
1180:         this.datasetToRangeAxisMap.put(
1181:             new Integer(index), new Integer(axisIndex)
1182:         );
1183:         // fake a dataset change event to update axes...
1184:         datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1185:     }
1186: 
1187:     /**
1188:      * Returns the renderer for the primary dataset.
1189:      *
1190:      * @return The item renderer (possibly <code>null</code>).
1191:      */
1192:     public XYItemRenderer getRenderer() {
1193:         return getRenderer(0);
1194:     }
1195: 
1196:     /**
1197:      * Returns the renderer for a dataset, or <code>null</code>.
1198:      *
1199:      * @param index  the renderer index.
1200:      *
1201:      * @return The renderer (possibly <code>null</code>).
1202:      */
1203:     public XYItemRenderer getRenderer(int index) {
1204:         XYItemRenderer result = null;
1205:         if (this.renderers.size() > index) {
1206:             result = (XYItemRenderer) this.renderers.get(index);
1207:         }
1208:         return result;
1209: 
1210:     }
1211: 
1212:     /**
1213:      * Sets the renderer for the primary dataset and sends a
1214:      * {@link PlotChangeEvent} to all registered listeners.  If the renderer
1215:      * is set to <code>null</code>, no data will be displayed.
1216:      *
1217:      * @param renderer  the renderer (<code>null</code> permitted).
1218:      */
1219:     public void setRenderer(XYItemRenderer renderer) {
1220:         setRenderer(0, renderer);
1221:     }
1222: 
1223:     /**
1224:      * Sets a renderer and sends a {@link PlotChangeEvent} to all
1225:      * registered listeners.
1226:      *
1227:      * @param index  the index.
1228:      * @param renderer  the renderer.
1229:      */
1230:     public void setRenderer(int index, XYItemRenderer renderer) {
1231:         setRenderer(index, renderer, true);
1232:     }
1233: 
1234:     /**
1235:      * Sets a renderer and sends a {@link PlotChangeEvent} to all
1236:      * registered listeners.
1237:      *
1238:      * @param index  the index.
1239:      * @param renderer  the renderer.
1240:      * @param notify  notify listeners?
1241:      */
1242:     public void setRenderer(int index, XYItemRenderer renderer, 
1243:                             boolean notify) {
1244:         XYItemRenderer existing = getRenderer(index);
1245:         if (existing != null) {
1246:             existing.removeChangeListener(this);
1247:         }
1248:         this.renderers.set(index, renderer);
1249:         if (renderer != null) {
1250:             renderer.setPlot(this);
1251:             renderer.addChangeListener(this);
1252:         }
1253:         configureDomainAxes();
1254:         configureRangeAxes();
1255:         if (notify) {
1256:             notifyListeners(new PlotChangeEvent(this));
1257:         }
1258:     }
1259: 
1260:     /**
1261:      * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1262:      * to all registered listeners.
1263:      * 
1264:      * @param renderers  the renderers.
1265:      */
1266:     public void setRenderers(XYItemRenderer[] renderers) {
1267:         for (int i = 0; i < renderers.length; i++) {
1268:             setRenderer(i, renderers[i], false);   
1269:         }
1270:         notifyListeners(new PlotChangeEvent(this));
1271:     }
1272:     
1273:     /**
1274:      * Returns the dataset rendering order.
1275:      *
1276:      * @return The order (never <code>null</code>).
1277:      */
1278:     public DatasetRenderingOrder getDatasetRenderingOrder() {
1279:         return this.datasetRenderingOrder;
1280:     }
1281: 
1282:     /**
1283:      * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1284:      * registered listeners.  By default, the plot renders the primary dataset
1285:      * last (so that the primary dataset overlays the secondary datasets).
1286:      * You can reverse this if you want to.
1287:      *
1288:      * @param order  the rendering order (<code>null</code> not permitted).
1289:      */
1290:     public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1291:         if (order == null) {
1292:             throw new IllegalArgumentException("Null 'order' argument.");
1293:         }
1294:         this.datasetRenderingOrder = order;
1295:         notifyListeners(new PlotChangeEvent(this));
1296:     }
1297: 
1298:     /**
1299:      * Returns the series rendering order.
1300:      *
1301:      * @return the order (never <code>null</code>).
1302:      */
1303:     public SeriesRenderingOrder getSeriesRenderingOrder() {
1304:         return this.seriesRenderingOrder;
1305:     }
1306: 
1307:     /**
1308:      * Sets the series order and sends a {@link PlotChangeEvent} to all
1309:      * registered listeners.  By default, the plot renders the primary series
1310:      * last (so that the primary series appears to be on top).
1311:      * You can reverse this if you want to.
1312:      *
1313:      * @param order  the rendering order (<code>null</code> not permitted).
1314:      */
1315:     public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1316:         if (order == null) {
1317:             throw new IllegalArgumentException("Null 'order' argument.");
1318:         }
1319:         this.seriesRenderingOrder = order;
1320:         notifyListeners(new PlotChangeEvent(this));
1321:     }
1322: 
1323:     /**
1324:      * Returns the index of the specified renderer, or <code>-1</code> if the
1325:      * renderer is not assigned to this plot.
1326:      *
1327:      * @param renderer  the renderer (<code>null</code> permitted).
1328:      *
1329:      * @return The renderer index.
1330:      */
1331:     public int getIndexOf(XYItemRenderer renderer) {
1332:         return this.renderers.indexOf(renderer);
1333:     }
1334: 
1335:     /**
1336:      * Returns the renderer for the specified dataset.  The code first
1337:      * determines the index of the dataset, then checks if there is a
1338:      * renderer with the same index (if not, the method returns renderer(0).
1339:      *
1340:      * @param dataset  the dataset (<code>null</code> permitted).
1341:      *
1342:      * @return The renderer (possibly <code>null</code>).
1343:      */
1344:     public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1345:         XYItemRenderer result = null;
1346:         for (int i = 0; i < this.datasets.size(); i++) {
1347:             if (this.datasets.get(i) == dataset) {
1348:                 result = (XYItemRenderer) this.renderers.get(i);
1349:                 if (result == null) {
1350:                     result = getRenderer();
1351:                 }
1352:                 break;
1353:             }
1354:         }
1355:         return result;
1356:     }
1357: 
1358:     /**
1359:      * Returns the weight for this plot when it is used as a subplot within a
1360:      * combined plot.
1361:      *
1362:      * @return The weight.
1363:      */
1364:     public int getWeight() {
1365:         return this.weight;
1366:     }
1367: 
1368:     /**
1369:      * Sets the weight for the plot.
1370:      *
1371:      * @param weight  the weight.
1372:      */
1373:     public void setWeight(int weight) {
1374:         this.weight = weight;
1375:     }
1376: 
1377:     /**
1378:      * Returns <code>true</code> if the domain gridlines are visible, and
1379:      * <code>false<code> otherwise.
1380:      *
1381:      * @return <code>true</code> or <code>false</code>.
1382:      */
1383:     public boolean isDomainGridlinesVisible() {
1384:         return this.domainGridlinesVisible;
1385:     }
1386: 
1387:     /**
1388:      * Sets the flag that controls whether or not the domain grid-lines are
1389:      * visible.
1390:      * <p>
1391:      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1392:      * registered listeners.
1393:      *
1394:      * @param visible  the new value of the flag.
1395:      */
1396:     public void setDomainGridlinesVisible(boolean visible) {
1397:         if (this.domainGridlinesVisible != visible) {
1398:             this.domainGridlinesVisible = visible;
1399:             notifyListeners(new PlotChangeEvent(this));
1400:         }
1401:     }
1402: 
1403:     /**
1404:      * Returns the stroke for the grid-lines (if any) plotted against the
1405:      * domain axis.
1406:      *
1407:      * @return The stroke.
1408:      */
1409:     public Stroke getDomainGridlineStroke() {
1410:         return this.domainGridlineStroke;
1411:     }
1412: 
1413:     /**
1414:      * Sets the stroke for the grid lines plotted against the domain axis.
1415:      * <p>
1416:      * If you set this to <code>null</code>, no grid lines will be drawn.
1417:      *
1418:      * @param stroke  the stroke (<code>null</code> permitted).
1419:      */
1420:     public void setDomainGridlineStroke(Stroke stroke) {
1421:         this.domainGridlineStroke = stroke;
1422:         notifyListeners(new PlotChangeEvent(this));
1423:     }
1424: 
1425:     /**
1426:      * Returns the paint for the grid lines (if any) plotted against the domain
1427:      * axis.
1428:      *
1429:      * @return The paint.
1430:      */
1431:     public Paint getDomainGridlinePaint() {
1432:         return this.domainGridlinePaint;
1433:     }
1434: 
1435:     /**
1436:      * Sets the paint for the grid lines plotted against the domain axis.
1437:      * <p>
1438:      * If you set this to <code>null</code>, no grid lines will be drawn.
1439:      *
1440:      * @param paint  the paint (<code>null</code> permitted).
1441:      */
1442:     public void setDomainGridlinePaint(Paint paint) {
1443:         this.domainGridlinePaint = paint;
1444:         notifyListeners(new PlotChangeEvent(this));
1445:     }
1446: 
1447:     /**
1448:      * Returns <code>true</code> if the range axis grid is visible, and
1449:      * <code>false<code> otherwise.
1450:      *
1451:      * @return A boolean.
1452:      */
1453:     public boolean isRangeGridlinesVisible() {
1454:         return this.rangeGridlinesVisible;
1455:     }
1456: 
1457:     /**
1458:      * Sets the flag that controls whether or not the range axis grid lines
1459:      * are visible.
1460:      * <p>
1461:      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1462:      * registered listeners.
1463:      *
1464:      * @param visible  the new value of the flag.
1465:      */
1466:     public void setRangeGridlinesVisible(boolean visible) {
1467:         if (this.rangeGridlinesVisible != visible) {
1468:             this.rangeGridlinesVisible = visible;
1469:             notifyListeners(new PlotChangeEvent(this));
1470:         }
1471:     }
1472: 
1473:     /**
1474:      * Returns the stroke for the grid lines (if any) plotted against the
1475:      * range axis.
1476:      *
1477:      * @return The stroke (never <code>null</code>).
1478:      */
1479:     public Stroke getRangeGridlineStroke() {
1480:         return this.rangeGridlineStroke;
1481:     }
1482: 
1483:     /**
1484:      * Sets the stroke for the grid lines plotted against the range axis,
1485:      * and sends a {@link PlotChangeEvent} to all registered listeners.
1486:      *
1487:      * @param stroke  the stroke (<code>null</code> not permitted).
1488:      */
1489:     public void setRangeGridlineStroke(Stroke stroke) {
1490:         if (stroke == null) {
1491:             throw new IllegalArgumentException("Null 'stroke' argument.");
1492:         }
1493:         this.rangeGridlineStroke = stroke;
1494:         notifyListeners(new PlotChangeEvent(this));
1495:     }
1496: 
1497:     /**
1498:      * Returns the paint for the grid lines (if any) plotted against the range
1499:      * axis.
1500:      *
1501:      * @return The paint (never <code>null</code>).
1502:      */
1503:     public Paint getRangeGridlinePaint() {
1504:         return this.rangeGridlinePaint;
1505:     }
1506: 
1507:     /**
1508:      * Sets the paint for the grid lines plotted against the range axis and
1509:      * sends a {@link PlotChangeEvent} to all registered listeners.
1510:      *
1511:      * @param paint  the paint (<code>null</code> permitted).
1512:      */
1513:     public void setRangeGridlinePaint(Paint paint) {
1514:         this.rangeGridlinePaint = paint;
1515:         notifyListeners(new PlotChangeEvent(this));
1516:     }
1517: 
1518:     /**
1519:      * Returns a flag that controls whether or not a zero baseline is
1520:      * displayed for the range axis.
1521:      *
1522:      * @return A boolean.
1523:      */
1524:     public boolean isRangeZeroBaselineVisible() {
1525:         return this.rangeZeroBaselineVisible;
1526:     }
1527: 
1528:     /**
1529:      * Sets the flag that controls whether or not the zero baseline is
1530:      * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1531:      * all registered listeners.
1532:      *
1533:      * @param visible  the flag.
1534:      */
1535:     public void setRangeZeroBaselineVisible(boolean visible) {
1536:         this.rangeZeroBaselineVisible = visible;
1537:         notifyListeners(new PlotChangeEvent(this));
1538:     }
1539: 
1540:     /**
1541:      * Returns the stroke used for the zero baseline against the range axis.
1542:      *
1543:      * @return The stroke (never <code>null</code>).
1544:      */
1545:     public Stroke getRangeZeroBaselineStroke() {
1546:         return this.rangeZeroBaselineStroke;
1547:     }
1548: 
1549:     /**
1550:      * Sets the stroke for the zero baseline for the range axis,
1551:      * and sends a {@link PlotChangeEvent} to all registered listeners.
1552:      *
1553:      * @param stroke  the stroke (<code>null</code> not permitted).
1554:      */
1555:     public void setRangeZeroBaselineStroke(Stroke stroke) {
1556:         if (stroke == null) {
1557:             throw new IllegalArgumentException("Null 'stroke' argument.");
1558:         }
1559:         this.rangeZeroBaselineStroke = stroke;
1560:         notifyListeners(new PlotChangeEvent(this));
1561:     }
1562: 
1563:     /**
1564:      * Returns the paint for the zero baseline (if any) plotted against the
1565:      * range axis.
1566:      *
1567:      * @return The paint (never <code>null</code>).
1568:      */
1569:     public Paint getRangeZeroBaselinePaint() {
1570:         return this.rangeZeroBaselinePaint;
1571:     }
1572: 
1573:     /**
1574:      * Sets the paint for the zero baseline plotted against the range axis and
1575:      * sends a {@link PlotChangeEvent} to all registered listeners.
1576:      *
1577:      * @param paint  the paint (<code>null</code> permitted).
1578:      */
1579:     public void setRangeZeroBaselinePaint(Paint paint) {
1580:         this.rangeZeroBaselinePaint = paint;
1581:         notifyListeners(new PlotChangeEvent(this));
1582:     }
1583: 
1584:     /**
1585:      * Returns the paint used for the domain tick bands.  If this is
1586:      * <code>null</code>, no tick bands will be drawn.
1587:      *
1588:      * @return The paint (possibly <code>null</code>).
1589:      */
1590:     public Paint getDomainTickBandPaint() {
1591:         return this.domainTickBandPaint;
1592:     }
1593: 
1594:     /**
1595:      * Sets the paint for the domain tick bands.
1596:      *
1597:      * @param paint  the paint (<code>null</code> permitted).
1598:      */
1599:     public void setDomainTickBandPaint(Paint paint) {
1600:         this.domainTickBandPaint = paint;
1601:         notifyListeners(new PlotChangeEvent(this));
1602:     }
1603: 
1604:     /**
1605:      * Returns the paint used for the range tick bands.  If this is
1606:      * <code>null</code>, no tick bands will be drawn.
1607:      *
1608:      * @return The paint (possibly <code>null</code>).
1609:      */
1610:     public Paint getRangeTickBandPaint() {
1611:         return this.rangeTickBandPaint;
1612:     }
1613: 
1614:     /**
1615:      * Sets the paint for the range tick bands.
1616:      *
1617:      * @param paint  the paint (<code>null</code> permitted).
1618:      */
1619:     public void setRangeTickBandPaint(Paint paint) {
1620:         this.rangeTickBandPaint = paint;
1621:         notifyListeners(new PlotChangeEvent(this));
1622:     }
1623: 
1624:     /**
1625:      * Returns the origin for the quadrants that can be displayed on the plot.
1626:      * This defaults to (0, 0).
1627:      *
1628:      * @return The origin point (never <code>null</code>).
1629:      */
1630:     public Point2D getQuadrantOrigin() {
1631:         return this.quadrantOrigin;
1632:     }
1633: 
1634:     /**
1635:      * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
1636:      * registered listeners.
1637:      *
1638:      * @param origin  the origin (<code>null</code> not permitted).
1639:      */
1640:     public void setQuadrantOrigin(Point2D origin) {
1641:         if (origin == null) {
1642:             throw new IllegalArgumentException("Null 'origin' argument.");
1643:         }
1644:         this.quadrantOrigin = origin;
1645:         notifyListeners(new PlotChangeEvent(this));
1646:     }
1647: 
1648:     /**
1649:      * Returns the paint used for the specified quadrant.
1650:      *
1651:      * @param index  the quadrant index (0-3).
1652:      *
1653:      * @return The paint (possibly <code>null</code>).
1654:      */
1655:     public Paint getQuadrantPaint(int index) {
1656:         if (index < 0 || index > 3) {
1657:             throw new IllegalArgumentException(
1658:                 "The index should be in the range 0 to 3."
1659:             );
1660:         }
1661:         return this.quadrantPaint[index];
1662:     }
1663: 
1664:     /**
1665:      * Sets the paint used for the specified quadrant and sends a
1666:      * {@link PlotChangeEvent} to all registered listeners.
1667:      *
1668:      * @param index  the quadrant index (0-3).
1669:      * @param paint  the paint (<code>null</code> permitted).
1670:      */
1671:     public void setQuadrantPaint(int index, Paint paint) {
1672:         if (index < 0 || index > 3) {
1673:             throw new IllegalArgumentException(
1674:                 "The index should be in the range 0 to 3."
1675:             );
1676:         }
1677:         this.quadrantPaint[index] = paint;
1678:         notifyListeners(new PlotChangeEvent(this));
1679:     }
1680: 
1681:     /**
1682:      * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
1683:      * to all registered listeners.
1684:      * <P>
1685:      * Typically a marker will be drawn by the renderer as a line perpendicular
1686:      * to the range axis, however this is entirely up to the renderer.
1687:      *
1688:      * @param marker  the marker (<code>null</code> not permitted).
1689:      */
1690:     public void addDomainMarker(Marker marker) {
1691:         // defer argument checking...
1692:         addDomainMarker(marker, Layer.FOREGROUND);
1693:     }
1694: 
1695:     /**
1696:      * Adds a marker for the domain axis in the specified layer and sends a
1697:      * {@link PlotChangeEvent} to all registered listeners.
1698:      * <P>
1699:      * Typically a marker will be drawn by the renderer as a line perpendicular
1700:      * to the range axis, however this is entirely up to the renderer.
1701:      *
1702:      * @param marker  the marker (<code>null</code> not permitted).
1703:      * @param layer  the layer (foreground or background).
1704:      */
1705:     public void addDomainMarker(Marker marker, Layer layer) {
1706:         addDomainMarker(0, marker, layer);
1707:     }
1708: 
1709:     /**
1710:      * Clears all the (foreground and background) domain markers and sends a
1711:      * {@link PlotChangeEvent} to all registered listeners.
1712:      */
1713:     public void clearDomainMarkers() {
1714:         if (this.foregroundDomainMarkers != null) {
1715:             this.foregroundDomainMarkers.clear();
1716:         }
1717:         if (this.backgroundDomainMarkers != null) {
1718:             this.backgroundDomainMarkers.clear();
1719:         }
1720:         notifyListeners(new PlotChangeEvent(this));
1721:     }
1722: 
1723:     /**
1724:      * Clears the (foreground and background) domain markers for a particular
1725:      * renderer.
1726:      *
1727:      * @param index  the renderer index.
1728:      */
1729:     public void clearDomainMarkers(int index) {
1730:         Integer key = new Integer(index);
1731:         if (this.backgroundDomainMarkers != null) {
1732:             Collection markers
1733:                 = (Collection) this.backgroundDomainMarkers.get(key);
1734:             if (markers != null) {
1735:                 markers.clear();
1736:             }
1737:         }
1738:         if (this.foregroundRangeMarkers != null) {
1739:             Collection markers
1740:                 = (Collection) this.foregroundDomainMarkers.get(key);
1741:             if (markers != null) {
1742:                 markers.clear();
1743:             }
1744:         }
1745:         notifyListeners(new PlotChangeEvent(this));
1746:     }
1747: 
1748:     /**
1749:      * Adds a marker for a renderer and sends a {@link PlotChangeEvent} to
1750:      * all registered listeners.
1751:      * <P>
1752:      * Typically a marker will be drawn by the renderer as a line perpendicular
1753:      * to the domain axis (that the renderer is mapped to), however this is
1754:      * entirely up to the renderer.
1755:      *
1756:      * @param index  the renderer index.
1757:      * @param marker  the marker.
1758:      * @param layer  the layer (foreground or background).
1759:      */
1760:     public void addDomainMarker(int index, Marker marker, Layer layer) {
1761:         Collection markers;
1762:         if (layer == Layer.FOREGROUND) {
1763:             markers = (Collection) this.foregroundDomainMarkers.get(
1764:                 new Integer(index)
1765:             );
1766:             if (markers == null) {
1767:                 markers = new java.util.ArrayList();
1768:                 this.foregroundDomainMarkers.put(new Integer(index), markers);
1769:             }
1770:             markers.add(marker);
1771:         }
1772:         else if (layer == Layer.BACKGROUND) {
1773:             markers = (Collection) this.backgroundDomainMarkers.get(
1774:                 new Integer(index)
1775:             );
1776:             if (markers == null) {
1777:                 markers = new java.util.ArrayList();
1778:                 this.backgroundDomainMarkers.put(new Integer(index), markers);
1779:             }
1780:             markers.add(marker);
1781:         }
1782:         notifyListeners(new PlotChangeEvent(this));
1783:     }
1784: 
1785:     /**
1786:      * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
1787:      * all registered listeners.
1788:      * <P>
1789:      * Typically a marker will be drawn by the renderer as a line perpendicular
1790:      * to the range axis, however this is entirely up to the renderer.
1791:      *
1792:      * @param marker  the marker (<code>null</code> not permitted).
1793:      */
1794:     public void addRangeMarker(Marker marker) {
1795:         addRangeMarker(marker, Layer.FOREGROUND);
1796:     }
1797: 
1798:     /**
1799:      * Adds a marker for the range axis in the specified layer and sends a
1800:      * {@link PlotChangeEvent} to all registered listeners.
1801:      * <P>
1802:      * Typically a marker will be drawn by the renderer as a line perpendicular
1803:      * to the range axis, however this is entirely up to the renderer.
1804:      *
1805:      * @param marker  the marker (<code>null</code> not permitted).
1806:      * @param layer  the layer (foreground or background).
1807:      */
1808:     public void addRangeMarker(Marker marker, Layer layer) {
1809:         addRangeMarker(0, marker, layer);
1810:     }
1811: 
1812:     /**
1813:      * Clears all the range markers and sends a {@link PlotChangeEvent} to all
1814:      * registered listeners.
1815:      */
1816:     public void clearRangeMarkers() {
1817:         if (this.foregroundRangeMarkers != null) {
1818:             this.foregroundRangeMarkers.clear();
1819:         }
1820:         if (this.backgroundRangeMarkers != null) {
1821:             this.backgroundRangeMarkers.clear();
1822:         }
1823:         notifyListeners(new PlotChangeEvent(this));
1824:     }
1825: 
1826:     /**
1827:      * Adds a marker for a renderer and sends a {@link PlotChangeEvent} to
1828:      * all registered listeners.
1829:      * <P>
1830:      * Typically a marker will be drawn by the renderer as a line perpendicular
1831:      * to the range axis, however this is entirely up to the renderer.
1832:      *
1833:      * @param index  the renderer index.
1834:      * @param marker  the marker.
1835:      * @param layer  the layer (foreground or background).
1836:      */
1837:     public void addRangeMarker(int index, Marker marker, Layer layer) {
1838:         Collection markers;
1839:         if (layer == Layer.FOREGROUND) {
1840:             markers = (Collection) this.foregroundRangeMarkers.get(
1841:                 new Integer(index)
1842:             );
1843:             if (markers == null) {
1844:                 markers = new java.util.ArrayList();
1845:                 this.foregroundRangeMarkers.put(new Integer(index), markers);
1846:             }
1847:             markers.add(marker);
1848:         }
1849:         else if (layer == Layer.BACKGROUND) {
1850:             markers = (Collection) this.backgroundRangeMarkers.get(
1851:                 new Integer(index)
1852:             );
1853:             if (markers == null) {
1854:                 markers = new java.util.ArrayList();
1855:                 this.backgroundRangeMarkers.put(new Integer(index), markers);
1856:             }
1857:             markers.add(marker);
1858:         }
1859:         notifyListeners(new PlotChangeEvent(this));
1860:     }
1861: 
1862:     /**
1863:      * Clears the (foreground and background) range markers for a particular
1864:      * renderer.
1865:      *
1866:      * @param index  the renderer index.
1867:      */
1868:     public void clearRangeMarkers(int index) {
1869:         Integer key = new Integer(index);
1870:         if (this.backgroundRangeMarkers != null) {
1871:             Collection markers
1872:                 = (Collection) this.backgroundRangeMarkers.get(key);
1873:             if (markers != null) {
1874:                 markers.clear();
1875:             }
1876:         }
1877:         if (this.foregroundRangeMarkers != null) {
1878:             Collection markers
1879:                 = (Collection) this.foregroundRangeMarkers.get(key);
1880:             if (markers != null) {
1881:                 markers.clear();
1882:             }
1883:         }
1884:         notifyListeners(new PlotChangeEvent(this));
1885:     }
1886: 
1887:     /**
1888:      * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
1889:      * registered listeners.
1890:      *
1891:      * @param annotation  the annotation (<code>null</code> not permitted).
1892:      */
1893:     public void addAnnotation(XYAnnotation annotation) {
1894:         if (annotation == null) {
1895:             throw new IllegalArgumentException("Null 'annotation' argument.");
1896:         }
1897:         this.annotations.add(annotation);
1898:         notifyListeners(new PlotChangeEvent(this));
1899:     }
1900: 
1901:     /**
1902:      * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
1903:      * to all registered listeners.
1904:      *
1905:      * @param annotation  the annotation (<code>null</code> not permitted).
1906:      *
1907:      * @return A boolean (indicates whether or not the annotation was removed).
1908:      */
1909:     public boolean removeAnnotation(XYAnnotation annotation) {
1910:         if (annotation == null) {
1911:             throw new IllegalArgumentException("Null 'annotation' argument.");
1912:         }
1913:         boolean removed = this.annotations.remove(annotation);
1914:         if (removed) {
1915:             notifyListeners(new PlotChangeEvent(this));
1916:         }
1917:         return removed;
1918:     }
1919: 
1920:     /**
1921:      * Returns the list of annotations.
1922:      *
1923:      * @return The list of annotations.
1924:      * 
1925:      * @since 1.0.1
1926:      */
1927:     public List getAnnotations() {
1928:         return new ArrayList(this.annotations);
1929:     }
1930: 
1931:     /**
1932:      * Clears all the annotations and sends a {@link PlotChangeEvent} to all
1933:      * registered listeners.
1934:      */
1935:     public void clearAnnotations() {
1936:         this.annotations.clear();
1937:         notifyListeners(new PlotChangeEvent(this));
1938:     }
1939: 
1940:     /**
1941:      * Calculates the space required for all the axes in the plot.
1942:      *
1943:      * @param g2  the graphics device.
1944:      * @param plotArea  the plot area.
1945:      *
1946:      * @return The required space.
1947:      */
1948:     protected AxisSpace calculateAxisSpace(Graphics2D g2,
1949:                                            Rectangle2D plotArea) {
1950:         AxisSpace space = new AxisSpace();
1951:         space = calculateDomainAxisSpace(g2, plotArea, space);
1952:         space = calculateRangeAxisSpace(g2, plotArea, space);
1953:         return space;
1954:     }
1955: 
1956:     /**
1957:      * Calculates the space required for the domain axis/axes.
1958:      *
1959:      * @param g2  the graphics device.
1960:      * @param plotArea  the plot area.
1961:      * @param space  a carrier for the result (<code>null</code> permitted).
1962:      *
1963:      * @return The required space.
1964:      */
1965:     protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
1966:                                                  Rectangle2D plotArea,
1967:                                                  AxisSpace space) {
1968: 
1969:         if (space == null) {
1970:             space = new AxisSpace();
1971:         }
1972: 
1973:         // reserve some space for the domain axis...
1974:         if (this.fixedDomainAxisSpace != null) {
1975:             if (this.orientation == PlotOrientation.HORIZONTAL) {
1976:                 space.ensureAtLeast(
1977:                     this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT
1978:                 );
1979:                 space.ensureAtLeast(
1980:                     this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT
1981:                 );
1982:             }
1983:             else if (this.orientation == PlotOrientation.VERTICAL) {
1984:                 space.ensureAtLeast(
1985:                     this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP
1986:                 );
1987:                 space.ensureAtLeast(
1988:                     this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM
1989:                 );
1990:             }
1991:         }
1992:         else {
1993:             // reserve space for the domain axes...
1994:             for (int i = 0; i < this.domainAxes.size(); i++) {
1995:                 Axis axis = (Axis) this.domainAxes.get(i);
1996:                 if (axis != null) {
1997:                     RectangleEdge edge = getDomainAxisEdge(i);
1998:                     space = axis.reserveSpace(g2, this, plotArea, edge, space);
1999:                 }
2000:             }
2001:         }
2002: 
2003:         return space;
2004: 
2005:     }
2006: 
2007:     /**
2008:      * Calculates the space required for the range axis/axes.
2009:      *
2010:      * @param g2  the graphics device.
2011:      * @param plotArea  the plot area.
2012:      * @param space  a carrier for the result (<code>null</code> permitted).
2013:      *
2014:      * @return The required space.
2015:      */
2016:     protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
2017:                                                 Rectangle2D plotArea,
2018:                                                 AxisSpace space) {
2019: 
2020:         if (space == null) {
2021:             space = new AxisSpace();
2022:         }
2023: 
2024:         // reserve some space for the range axis...
2025:         if (this.fixedRangeAxisSpace != null) {
2026:             if (this.orientation == PlotOrientation.HORIZONTAL) {
2027:                 space.ensureAtLeast(
2028:                     this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP
2029:                 );
2030:                 space.ensureAtLeast(
2031:                     this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM
2032:                 );
2033:             }
2034:             else if (this.orientation == PlotOrientation.VERTICAL) {
2035:                 space.ensureAtLeast(
2036:                     this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT
2037:                 );
2038:                 space.ensureAtLeast(
2039:                     this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT
2040:                 );
2041:             }
2042:         }
2043:         else {
2044:             // reserve space for the range axes...
2045:             for (int i = 0; i < this.rangeAxes.size(); i++) {
2046:                 Axis axis = (Axis) this.rangeAxes.get(i);
2047:                 if (axis != null) {
2048:                     RectangleEdge edge = getRangeAxisEdge(i);
2049:                     space = axis.reserveSpace(g2, this, plotArea, edge, space);
2050:                 }
2051:             }
2052:         }
2053:         return space;
2054: 
2055:     }
2056: 
2057:     /**
2058:      * Draws the plot within the specified area on a graphics device.
2059:      *
2060:      * @param g2  the graphics device.
2061:      * @param area  the plot area (in Java2D space).
2062:      * @param anchor  an anchor point in Java2D space (<code>null</code>
2063:      *                permitted).
2064:      * @param parentState  the state from the parent plot, if there is one
2065:      *                     (<code>null</code> permitted).
2066:      * @param info  collects chart drawing information (<code>null</code>
2067:      *              permitted).
2068:      */
2069:     public void draw(Graphics2D g2,
2070:                      Rectangle2D area,
2071:                      Point2D anchor,
2072:                      PlotState parentState,
2073:                      PlotRenderingInfo info) {
2074: 
2075:         // if the plot area is too small, just return...
2076:         boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2077:         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2078:         if (b1 || b2) {
2079:             return;
2080:         }
2081: 
2082:         // record the plot area...
2083:         if (info != null) {
2084:             info.setPlotArea(area);
2085:         }
2086: 
2087:         // adjust the drawing area for the plot insets (if any)...
2088:         RectangleInsets insets = getInsets();
2089:         insets.trim(area);
2090: 
2091:         AxisSpace space = calculateAxisSpace(g2, area);
2092:         Rectangle2D dataArea = space.shrink(area, null);
2093:         this.axisOffset.trim(dataArea);
2094: 
2095:         if (info != null) {
2096:             info.setDataArea(dataArea);
2097:         }
2098: 
2099:         // draw the plot background and axes...
2100:         drawBackground(g2, dataArea);
2101:         Map axisStateMap = drawAxes(g2, area, dataArea, info);
2102: 
2103:         if (anchor != null && !dataArea.contains(anchor)) {
2104:             anchor = null;
2105:         }
2106:         CrosshairState crosshairState = new CrosshairState();
2107:         crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
2108:         crosshairState.setAnchor(anchor);
2109:         crosshairState.setCrosshairX(getDomainCrosshairValue());
2110:         crosshairState.setCrosshairY(getRangeCrosshairValue());
2111:         Shape originalClip = g2.getClip();
2112:         Composite originalComposite = g2.getComposite();
2113: 
2114:         g2.clip(dataArea);
2115:         g2.setComposite(
2116:             AlphaComposite.getInstance(
2117:                 AlphaComposite.SRC_OVER, getForegroundAlpha()
2118:             )
2119:         );
2120: 
2121:         AxisState domainAxisState
2122:             = (AxisState) axisStateMap.get(getDomainAxis());
2123:         if (domainAxisState == null) {
2124:             if (parentState != null) {
2125:                 domainAxisState
2126:                     = (AxisState) parentState.getSharedAxisStates().get(
2127:                         getDomainAxis()
2128:                     );
2129:             }
2130:         }
2131: 
2132:         AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis());
2133:         if (rangeAxisState == null) {
2134:             if (parentState != null) {
2135:                 rangeAxisState
2136:                     = (AxisState) parentState.getSharedAxisStates().get(
2137:                         getRangeAxis()
2138:                     );
2139:             }
2140:         }
2141:         if (domainAxisState != null) {
2142:             drawDomainTickBands(g2, dataArea, domainAxisState.getTicks());
2143:         }
2144:         if (rangeAxisState != null) {
2145:             drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks());
2146:         }
2147:         if (domainAxisState != null) {
2148:             drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
2149:         }
2150:         if (rangeAxisState != null) {
2151:             drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2152:             drawZeroRangeBaseline(g2, dataArea);
2153:         }
2154: 
2155:         // draw the markers that are associated with a specific renderer...
2156:         for (int i = 0; i < this.renderers.size(); i++) {
2157:             drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2158:         }
2159:         for (int i = 0; i < this.renderers.size(); i++) {
2160:             drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2161:         }
2162: 
2163:         // now draw annotations and render data items...
2164:         boolean foundData = false;
2165:         DatasetRenderingOrder order = getDatasetRenderingOrder();
2166:         if (order == DatasetRenderingOrder.FORWARD) {
2167: 
2168:             // draw background annotations
2169:             int rendererCount = this.renderers.size();
2170:             for (int i = 0; i < rendererCount; i++) {
2171:                 XYItemRenderer r = getRenderer(i);
2172:                 if (r != null) {
2173:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2174:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2175:                     r.drawAnnotations(
2176:                         g2, dataArea, domainAxis, rangeAxis,
2177:                         Layer.BACKGROUND, info
2178:                     );
2179:                 }
2180:             }
2181: 
2182:             // render data items...
2183:             for (int i = 0; i < getDatasetCount(); i++) {
2184:                 foundData = render(g2, dataArea, i, info, crosshairState)
2185:                     || foundData;
2186:             }
2187: 
2188:             // draw foreground annotations
2189:             for (int i = 0; i < rendererCount; i++) {
2190:                 XYItemRenderer r = getRenderer(i);
2191:                 if (r != null) {
2192:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2193:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2194:                     r.drawAnnotations(
2195:                         g2, dataArea, domainAxis, rangeAxis,
2196:                         Layer.FOREGROUND, info
2197:                     );
2198:                 }
2199:             }
2200: 
2201:         }
2202:         else if (order == DatasetRenderingOrder.REVERSE) {
2203: 
2204:             // draw background annotations
2205:             int rendererCount = this.renderers.size();
2206:             for (int i = rendererCount - 1; i >= 0; i--) {
2207:                 XYItemRenderer r = getRenderer(i);
2208:                 if (r != null) {
2209:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2210:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2211:                     r.drawAnnotations(
2212:                         g2, dataArea, domainAxis, rangeAxis,
2213:                         Layer.BACKGROUND, info
2214:                     );
2215:                 }
2216:             }
2217: 
2218:             for (int i = getDatasetCount() - 1; i >= 0; i--) {
2219:                 foundData = render(g2, dataArea, i, info, crosshairState)
2220:                     || foundData;
2221:             }
2222: 
2223:             // draw foreground annotations
2224:             for (int i = rendererCount - 1; i >= 0; i--) {
2225:                 XYItemRenderer r = getRenderer(i);
2226:                 if (r != null) {
2227:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2228:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2229:                     r.drawAnnotations(
2230:                         g2, dataArea, domainAxis, rangeAxis,
2231:                         Layer.FOREGROUND, info
2232:                     );
2233:                 }
2234:             }
2235: 
2236:         }
2237: 
2238:         PlotOrientation orient = getOrientation();
2239: 
2240:         // draw domain crosshair if required...
2241:         if (!this.domainCrosshairLockedOnData && anchor != null) {
2242:             double xx = getDomainAxis().java2DToValue(
2243:                 anchor.getX(), dataArea, getDomainAxisEdge()
2244:             );
2245:             crosshairState.setCrosshairX(xx);
2246:         }
2247:         setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
2248:         if (isDomainCrosshairVisible()) {
2249:             double x = getDomainCrosshairValue();
2250:             Paint paint = getDomainCrosshairPaint();
2251:             Stroke stroke = getDomainCrosshairStroke();
2252:             if (orient == PlotOrientation.HORIZONTAL) {
2253:                 drawHorizontalLine(g2, dataArea, x, stroke, paint);
2254:             }
2255:             else if (orient == PlotOrientation.VERTICAL) {
2256:                 drawVerticalLine(g2, dataArea, x, stroke, paint);
2257:             }
2258:         }
2259: 
2260:         // draw range crosshair if required...
2261:         if (!this.rangeCrosshairLockedOnData && anchor != null) {
2262:             double yy = getRangeAxis().java2DToValue(
2263:                 anchor.getY(), dataArea, getRangeAxisEdge()
2264:             );
2265:             crosshairState.setCrosshairY(yy);
2266:         }
2267:         setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
2268:         if (isRangeCrosshairVisible()
2269:             && getRangeAxis().getRange().contains(getRangeCrosshairValue())) {
2270:             double y = getRangeCrosshairValue();
2271:             Paint paint = getRangeCrosshairPaint();
2272:             Stroke stroke = getRangeCrosshairStroke();
2273:             if (orient == PlotOrientation.HORIZONTAL) {
2274:                 drawVerticalLine(g2, dataArea, y, stroke, paint);
2275:             }
2276:             else if (orient == PlotOrientation.VERTICAL) {
2277:                 drawHorizontalLine(g2, dataArea, y, stroke, paint);
2278:             }
2279:         }
2280: 
2281:         if (!foundData) {
2282:             drawNoDataMessage(g2, dataArea);
2283:         }
2284: 
2285:         for (int i = 0; i < this.renderers.size(); i++) {
2286:             drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2287:         }
2288:         for (int i = 0; i < this.renderers.size(); i++) {
2289:             drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2290:         }
2291: 
2292:         drawAnnotations(g2, dataArea, info);
2293:         g2.setClip(originalClip);
2294:         g2.setComposite(originalComposite);
2295: 
2296:         drawOutline(g2, dataArea);
2297: 
2298:     }
2299: 
2300:     /**
2301:      * Draws the background for the plot.
2302:      *
2303:      * @param g2  the graphics device.
2304:      * @param area  the area.
2305:      */
2306:     public void drawBackground(Graphics2D g2, Rectangle2D area) {
2307:         fillBackground(g2, area);
2308:         drawQuadrants(g2, area);
2309:         drawBackgroundImage(g2, area);
2310:     }
2311: 
2312:     /**
2313:      * Draws the quadrants.
2314:      *
2315:      * @param g2  the graphics device.
2316:      * @param area  the area.
2317:      */
2318:     protected void drawQuadrants(Graphics2D g2, Rectangle2D area) {
2319:         //  0 | 1
2320:         //  --+--
2321:         //  2 | 3
2322:         boolean somethingToDraw = false;
2323: 
2324:         ValueAxis xAxis = getDomainAxis();
2325:         double x = this.quadrantOrigin.getX();
2326:         double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge());
2327: 
2328:         ValueAxis yAxis = getRangeAxis();
2329:         double y = this.quadrantOrigin.getY();
2330:         double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge());
2331: 
2332:         double xmin = xAxis.getLowerBound();
2333:         double xxmin = xAxis.valueToJava2D(xmin, area, getDomainAxisEdge());
2334: 
2335:         double xmax = xAxis.getUpperBound();
2336:         double xxmax = xAxis.valueToJava2D(xmax, area, getDomainAxisEdge());
2337: 
2338:         double ymin = yAxis.getLowerBound();
2339:         double yymin = yAxis.valueToJava2D(ymin, area, getRangeAxisEdge());
2340: 
2341:         double ymax = yAxis.getUpperBound();
2342:         double yymax = yAxis.valueToJava2D(ymax, area, getRangeAxisEdge());
2343: 
2344:         Rectangle2D[] r = new Rectangle2D[] {null, null, null, null};
2345:         if (this.quadrantPaint[0] != null) {
2346:             if (x > xmin && y < ymax) {
2347:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2348:                     r[0] = new Rectangle2D.Double(
2349:                         Math.min(yymax, yy), Math.min(xxmin, xx),
2350:                         Math.abs(yy - yymax), Math.abs(xx - xxmin)
2351:                     );
2352:                 }
2353:                 else {  // PlotOrientation.VERTICAL
2354:                     r[0] = new Rectangle2D.Double(
2355:                         Math.min(xxmin, xx), Math.min(yymax, yy),
2356:                         Math.abs(xx - xxmin), Math.abs(yy - yymax)
2357:                     );
2358:                 }
2359:                 somethingToDraw = true;
2360:             }
2361:         }
2362:         if (this.quadrantPaint[1] != null) {
2363:             if (x < xmax && y < ymax) {
2364:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2365:                     r[1] = new Rectangle2D.Double(
2366:                         Math.min(yymax, yy), Math.min(xxmax, xx),
2367:                         Math.abs(yy - yymax), Math.abs(xx - xxmax)
2368:                     );
2369:                 }
2370:                 else {  // PlotOrientation.VERTICAL
2371:                     r[1] = new Rectangle2D.Double(
2372:                         Math.min(xx, xxmax), Math.min(yymax, yy),
2373:                         Math.abs(xx - xxmax), Math.abs(yy - yymax)
2374:                     );
2375:                 }
2376:                 somethingToDraw = true;
2377:             }
2378:         }
2379:         if (this.quadrantPaint[2] != null) {
2380:             if (x > xmin && y > ymin) {
2381:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2382:                     r[2] = new Rectangle2D.Double(
2383:                         Math.min(yymin, yy), Math.min(xxmin, xx),
2384:                         Math.abs(yy - yymin), Math.abs(xx - xxmin)
2385:                     );
2386:                 }
2387:                 else {  // PlotOrientation.VERTICAL
2388:                     r[2] = new Rectangle2D.Double(
2389:                         Math.min(xxmin, xx), Math.min(yymin, yy),
2390:                         Math.abs(xx - xxmin), Math.abs(yy - yymin)
2391:                     );
2392:                 }
2393:                 somethingToDraw = true;
2394:             }
2395:         }
2396:         if (this.quadrantPaint[3] != null) {
2397:             if (x < xmax && y > ymin) {
2398:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2399:                     r[3] = new Rectangle2D.Double(
2400:                         Math.min(yymin, yy), Math.min(xxmax, xx),
2401:                         Math.abs(yy - yymin), Math.abs(xx - xxmax)
2402:                     );
2403:                 }
2404:                 else {  // PlotOrientation.VERTICAL
2405:                     r[3] = new Rectangle2D.Double(
2406:                         Math.min(xx, xxmax), Math.min(yymin, yy),
2407:                         Math.abs(xx - xxmax), Math.abs(yy - yymin)
2408:                     );
2409:                 }
2410:                 somethingToDraw = true;
2411:             }
2412:         }
2413:         if (somethingToDraw) {
2414:             Composite originalComposite = g2.getComposite();
2415:             g2.setComposite(
2416:                 AlphaComposite.getInstance(
2417:                     AlphaComposite.SRC_OVER, getBackgroundAlpha()
2418:                 )
2419:             );
2420:             for (int i = 0; i < 4; i++) {
2421:                 if (this.quadrantPaint[i] != null && r[i] != null) {
2422:                     g2.setPaint(this.quadrantPaint[i]);
2423:                     g2.fill(r[i]);
2424:                 }
2425:             }
2426:             g2.setComposite(originalComposite);
2427:         }
2428:     }
2429: 
2430:     /**
2431:      * Draws the domain tick bands, if any.
2432:      *
2433:      * @param g2  the graphics device.
2434:      * @param dataArea  the data area.
2435:      * @param ticks  the ticks.
2436:      */
2437:     public void drawDomainTickBands(Graphics2D g2, Rectangle2D dataArea,
2438:                                     List ticks) {
2439:         // draw the domain tick bands, if any...
2440:         Paint bandPaint = getDomainTickBandPaint();
2441:         if (bandPaint != null) {
2442:             boolean fillBand = false;
2443:             ValueAxis xAxis = getDomainAxis();
2444:             double previous = xAxis.getLowerBound();
2445:             Iterator iterator = ticks.iterator();
2446:             while (iterator.hasNext()) {
2447:                 ValueTick tick = (ValueTick) iterator.next();
2448:                 double current = tick.getValue();
2449:                 if (fillBand) {
2450:                     getRenderer().fillDomainGridBand(
2451:                         g2, this, xAxis, dataArea, previous, current
2452:                     );
2453:                 }
2454:                 previous = current;
2455:                 fillBand = !fillBand;
2456:             }
2457:             double end = xAxis.getUpperBound();
2458:             if (fillBand) {
2459:                 getRenderer().fillDomainGridBand(
2460:                     g2, this, xAxis, dataArea, previous, end
2461:                 );
2462:             }
2463:         }
2464:     }
2465: 
2466:     /**
2467:      * Draws the range tick bands, if any.
2468:      *
2469:      * @param g2  the graphics device.
2470:      * @param dataArea  the data area.
2471:      * @param ticks  the ticks.
2472:      */
2473:     public void drawRangeTickBands(Graphics2D g2, Rectangle2D dataArea,
2474:                                    List ticks) {
2475: 
2476:         // draw the range tick bands, if any...
2477:         Paint bandPaint = getRangeTickBandPaint();
2478:         if (bandPaint != null) {
2479:             boolean fillBand = false;
2480:             ValueAxis axis = getRangeAxis();
2481:             double previous = axis.getLowerBound();
2482:             Iterator iterator = ticks.iterator();
2483:             while (iterator.hasNext()) {
2484:                 ValueTick tick = (ValueTick) iterator.next();
2485:                 double current = tick.getValue();
2486:                 if (fillBand) {
2487:                     getRenderer().fillRangeGridBand(
2488:                         g2, this, axis, dataArea, previous, current
2489:                     );
2490:                 }
2491:                 previous = current;
2492:                 fillBand = !fillBand;
2493:             }
2494:             double end = axis.getUpperBound();
2495:             if (fillBand) {
2496:                 getRenderer().fillRangeGridBand(
2497:                     g2, this, axis, dataArea, previous, end
2498:                 );
2499:             }
2500:         }
2501:     }
2502: 
2503:     /**
2504:      * A utility method for drawing the axes.
2505:      *
2506:      * @param g2  the graphics device (<code>null</code> not permitted).
2507:      * @param plotArea  the plot area (<code>null</code> not permitted).
2508:      * @param dataArea  the data area (<code>null</code> not permitted).
2509:      * @param plotState  collects information about the plot (<code>null</code>
2510:      *                   permitted).
2511:      *
2512:      * @return A map containing the state for each axis drawn.
2513:      */
2514:     protected Map drawAxes(Graphics2D g2,
2515:                            Rectangle2D plotArea,
2516:                            Rectangle2D dataArea,
2517:                            PlotRenderingInfo plotState) {
2518: 
2519:         AxisCollection axisCollection = new AxisCollection();
2520: 
2521:         // add domain axes to lists...
2522:         for (int index = 0; index < this.domainAxes.size(); index++) {
2523:             ValueAxis axis = (ValueAxis) this.domainAxes.get(index);
2524:             if (axis != null) {
2525:                 axisCollection.add(axis, getDomainAxisEdge(index));
2526:             }
2527:         }
2528: 
2529:         // add range axes to lists...
2530:         for (int index = 0; index < this.rangeAxes.size(); index++) {
2531:             ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index);
2532:             if (yAxis != null) {
2533:                 axisCollection.add(yAxis, getRangeAxisEdge(index));
2534:             }
2535:         }
2536: 
2537:         Map axisStateMap = new HashMap();
2538: 
2539:         // draw the top axes
2540:         double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(
2541:             dataArea.getHeight()
2542:         );
2543:         Iterator iterator = axisCollection.getAxesAtTop().iterator();
2544:         while (iterator.hasNext()) {
2545:             ValueAxis axis = (ValueAxis) iterator.next();
2546:             AxisState info = axis.draw(
2547:                 g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState
2548:             );
2549:             cursor = info.getCursor();
2550:             axisStateMap.put(axis, info);
2551:         }
2552: 
2553:         // draw the bottom axes
2554:         cursor = dataArea.getMaxY()
2555:                  + this.axisOffset.calculateBottomOutset(dataArea.getHeight());
2556:         iterator = axisCollection.getAxesAtBottom().iterator();
2557:         while (iterator.hasNext()) {
2558:             ValueAxis axis = (ValueAxis) iterator.next();
2559:             AxisState info = axis.draw(
2560:                 g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM, plotState
2561:             );
2562:             cursor = info.getCursor();
2563:             axisStateMap.put(axis, info);
2564:         }
2565: 
2566:         // draw the left axes
2567:         cursor = dataArea.getMinX()
2568:                  - this.axisOffset.calculateLeftOutset(dataArea.getWidth());
2569:         iterator = axisCollection.getAxesAtLeft().iterator();
2570:         while (iterator.hasNext()) {
2571:             ValueAxis axis = (ValueAxis) iterator.next();
2572:             AxisState info = axis.draw(
2573:                 g2, cursor, plotArea, dataArea, RectangleEdge.LEFT, plotState
2574:              );
2575:             cursor = info.getCursor();
2576:             axisStateMap.put(axis, info);
2577:         }
2578: 
2579:         // draw the right axes
2580:         cursor = dataArea.getMaxX()
2581:                  + this.axisOffset.calculateRightOutset(dataArea.getWidth());
2582:         iterator = axisCollection.getAxesAtRight().iterator();
2583:         while (iterator.hasNext()) {
2584:             ValueAxis axis = (ValueAxis) iterator.next();
2585:             AxisState info = axis.draw(
2586:                 g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT, plotState
2587:             );
2588:             cursor = info.getCursor();
2589:             axisStateMap.put(axis, info);
2590:         }
2591: 
2592:         return axisStateMap;
2593:     }
2594: 
2595:     /**
2596:      * Draws a representation of the data within the dataArea region, using the
2597:      * current renderer.
2598:      * <P>
2599:      * The <code>info</code> and <code>crosshairState</code> arguments may be
2600:      * <code>null</code>.
2601:      *
2602:      * @param g2  the graphics device.
2603:      * @param dataArea  the region in which the data is to be drawn.
2604:      * @param index  the dataset index.
2605:      * @param info  an optional object for collection dimension information.
2606:      * @param crosshairState  collects crosshair information
2607:      *                        (<code>null</code> permitted).
2608:      *
2609:      * @return A flag that indicates whether any data was actually rendered.
2610:      */
2611:     public boolean render(Graphics2D g2,
2612:                           Rectangle2D dataArea,
2613:                           int index,
2614:                           PlotRenderingInfo info,
2615:                           CrosshairState crosshairState) {
2616: 
2617:         boolean foundData = false;
2618:         XYDataset dataset = getDataset(index);
2619:         if (!DatasetUtilities.isEmptyOrNull(dataset)) {
2620:             foundData = true;
2621:             ValueAxis xAxis = getDomainAxisForDataset(index);
2622:             ValueAxis yAxis = getRangeAxisForDataset(index);
2623:             XYItemRenderer renderer = getRenderer(index);
2624:             if (renderer == null) {
2625:                 renderer = getRenderer();
2626:             }
2627: 
2628:             XYItemRendererState state = renderer.initialise(
2629:                 g2, dataArea, this, dataset, info
2630:             );
2631:             int passCount = renderer.getPassCount();
2632: 
2633:             SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
2634:             if (seriesOrder == SeriesRenderingOrder.REVERSE) {
2635:                    //render series in reverse order
2636:                 for (int pass = 0; pass < passCount; pass++) {
2637:                     int seriesCount = dataset.getSeriesCount();
2638:                     for (int series = seriesCount-1; series >= 0 ; series--) {
2639:                         int itemCount = dataset.getItemCount(series);
2640:                         for (int item = 0; item < itemCount; item++) {
2641:                             renderer.drawItem(
2642:                                 g2, state, dataArea, info,
2643:                                 this, xAxis, yAxis, dataset, series, item,
2644:                                 crosshairState, pass
2645:                             );
2646:                         }
2647:                     }
2648:                 }
2649:             }
2650:             else {
2651:                    //render series in forward order
2652:                 for (int pass = 0; pass < passCount; pass++) {
2653:                     int seriesCount = dataset.getSeriesCount();
2654:                     for (int series = 0; series < seriesCount; series++) {
2655:                         int itemCount = dataset.getItemCount(series);
2656:                         for (int item = 0; item < itemCount; item++) {
2657:                             renderer.drawItem(
2658:                                 g2, state, dataArea, info,
2659:                                 this, xAxis, yAxis, dataset, series, item,
2660:                                 crosshairState, pass
2661:                             );
2662:                         }
2663:                     }
2664:                 }
2665:             }
2666:         }
2667:         return foundData;
2668:     }
2669: 
2670:     /**
2671:      * Returns the domain axis for a dataset.
2672:      *
2673:      * @param index  the dataset index.
2674:      *
2675:      * @return The axis.
2676:      */
2677:     public ValueAxis getDomainAxisForDataset(int index) {
2678: 
2679:         if (index < 0 || index >= getDatasetCount()) {
2680:             throw new IllegalArgumentException("Index 'index' out of bounds.");
2681:         }
2682: 
2683:         ValueAxis valueAxis = null;
2684:         Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(
2685:             new Integer(index)
2686:         );
2687:         if (axisIndex != null) {
2688:             valueAxis = getDomainAxis(axisIndex.intValue());
2689:         }
2690:         else {
2691:             valueAxis = getDomainAxis(0);
2692:         }
2693:         return valueAxis;
2694: 
2695:     }
2696: 
2697:     /**
2698:      * Returns the range axis for a dataset.
2699:      *
2700:      * @param index  the dataset index.
2701:      *
2702:      * @return The axis.
2703:      */
2704:     public ValueAxis getRangeAxisForDataset(int index) {
2705: 
2706:         if (index < 0 || index >= getDatasetCount()) {
2707:             throw new IllegalArgumentException("Index 'index' out of bounds.");
2708:         }
2709: 
2710:         ValueAxis valueAxis = null;
2711:         Integer axisIndex
2712:             = (Integer) this.datasetToRangeAxisMap.get(new Integer(index));
2713:         if (axisIndex != null) {
2714:             valueAxis = getRangeAxis(axisIndex.intValue());
2715:         }
2716:         else {
2717:             valueAxis = getRangeAxis(0);
2718:         }
2719:         return valueAxis;
2720: 
2721:     }
2722: 
2723:     /**
2724:      * Draws the gridlines for the plot, if they are visible.
2725:      *
2726:      * @param g2  the graphics device.
2727:      * @param dataArea  the data area.
2728:      * @param ticks  the ticks.
2729:      */
2730:     protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea,
2731:                                        List ticks) {
2732: 
2733:         // no renderer, no gridlines...
2734:         if (getRenderer() == null) {
2735:             return;
2736:         }
2737: 
2738:         // draw the domain grid lines, if any...
2739:         if (isDomainGridlinesVisible()) {
2740:             Stroke gridStroke = getDomainGridlineStroke();
2741:             Paint gridPaint = getDomainGridlinePaint();
2742:             if ((gridStroke != null) && (gridPaint != null)) {
2743:                 Iterator iterator = ticks.iterator();
2744:                 while (iterator.hasNext()) {
2745:                     ValueTick tick = (ValueTick) iterator.next();
2746:                     getRenderer().drawDomainGridLine(
2747:                         g2, this, getDomainAxis(), dataArea, tick.getValue()
2748:                     );
2749:                 }
2750:             }
2751:         }
2752:     }
2753: 
2754:     /**
2755:      * Draws the gridlines for the plot's primary range axis, if they are
2756:      * visible.
2757:      *
2758:      * @param g2  the graphics device.
2759:      * @param area  the data area.
2760:      * @param ticks  the ticks.
2761:      */
2762:     protected void drawRangeGridlines(Graphics2D g2, Rectangle2D area,
2763:                                       List ticks) {
2764: 
2765:         // draw the range grid lines, if any...
2766:         if (isRangeGridlinesVisible()) {
2767:             Stroke gridStroke = getRangeGridlineStroke();
2768:             Paint gridPaint = getRangeGridlinePaint();
2769:             ValueAxis axis = getRangeAxis();
2770:             if (axis != null) {
2771:                 Iterator iterator = ticks.iterator();
2772:                 while (iterator.hasNext()) {
2773:                     ValueTick tick = (ValueTick) iterator.next();
2774:                     if (tick.getValue() != 0.0
2775:                             || !isRangeZeroBaselineVisible()) {
2776:                         getRenderer().drawRangeLine(
2777:                             g2, this, getRangeAxis(), area,
2778:                             tick.getValue(), gridPaint, gridStroke
2779:                         );
2780:                     }
2781:                 }
2782:             }
2783:         }
2784:     }
2785: 
2786:     /**
2787:      * Draws a base line across the chart at value zero on the range axis.
2788:      *
2789:      * @param g2  the graphics device.
2790:      * @param area  the data area.
2791:      */
2792:     protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) {
2793:         if (isRangeZeroBaselineVisible()) {
2794:             getRenderer().drawRangeLine(
2795:                 g2, this, getRangeAxis(), area,
2796:                 0.0, this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke
2797:             );
2798:         }
2799:     }
2800: 
2801:     /**
2802:      * Draws the annotations for the plot.
2803:      *
2804:      * @param g2  the graphics device.
2805:      * @param dataArea  the data area.
2806:      * @param info  the chart rendering info.
2807:      */
2808:     public void drawAnnotations(Graphics2D g2,
2809:                                 Rectangle2D dataArea,
2810:                                 PlotRenderingInfo info) {
2811: 
2812:         Iterator iterator = this.annotations.iterator();
2813:         while (iterator.hasNext()) {
2814:             XYAnnotation annotation = (XYAnnotation) iterator.next();
2815:             ValueAxis xAxis = getDomainAxis();
2816:             ValueAxis yAxis = getRangeAxis();
2817:             annotation.draw(g2, this, dataArea, xAxis, yAxis, 0, info);
2818:         }
2819: 
2820:     }
2821: 
2822:     /**
2823:      * Draws the domain markers (if any) for an axis and layer.  This method is
2824:      * typically called from within the draw() method.
2825:      *
2826:      * @param g2  the graphics device.
2827:      * @param dataArea  the data area.
2828:      * @param index  the renderer index.
2829:      * @param layer  the layer (foreground or background).
2830:      */
2831:     protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea,
2832:                                      int index, Layer layer) {
2833: 
2834:         XYItemRenderer r = getRenderer(index);
2835:         if (r == null) {
2836:             return;
2837:         }
2838: 
2839:         Collection markers = getDomainMarkers(index, layer);
2840:         ValueAxis axis = getDomainAxisForDataset(index);
2841:         if (markers != null && axis != null) {
2842:             Iterator iterator = markers.iterator();
2843:             while (iterator.hasNext()) {
2844:                 Marker marker = (Marker) iterator.next();
2845:                 r.drawDomainMarker(g2, this, axis, marker, dataArea);
2846:             }
2847:         }
2848: 
2849:     }
2850: 
2851:     /**
2852:      * Draws the range markers (if any) for a renderer and layer.  This method
2853:      * is typically called from within the draw() method.
2854:      *
2855:      * @param g2  the graphics device.
2856:      * @param dataArea  the data area.
2857:      * @param index  the renderer index.
2858:      * @param layer  the layer (foreground or background).
2859:      */
2860:     protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea,
2861:                                     int index, Layer layer) {
2862: 
2863:         XYItemRenderer r = getRenderer(index);
2864:         if (r == null) {
2865:             return;
2866:         }
2867: 
2868:         Collection markers = getRangeMarkers(index, layer);
2869:         ValueAxis axis = getRangeAxis(index);
2870:         // TODO: get the axis that the renderer maps to
2871:         if (markers != null && axis != null) {
2872:             Iterator iterator = markers.iterator();
2873:             while (iterator.hasNext()) {
2874:                 Marker marker = (Marker) iterator.next();
2875:                 r.drawRangeMarker(g2, this, axis, marker, dataArea);
2876:             }
2877:         }
2878: 
2879:     }
2880: 
2881:     /**
2882:      * Returns the list of domain markers (read only) for the specified layer.
2883:      *
2884:      * @param layer  the layer (foreground or background).
2885:      *
2886:      * @return The list of domain markers.
2887:      */
2888:     public Collection getDomainMarkers(Layer layer) {
2889:         return getDomainMarkers(0, layer);
2890:     }
2891: 
2892:     /**
2893:      * Returns the list of range markers (read only) for the specified layer.
2894:      *
2895:      * @param layer  the layer (foreground or background).
2896:      *
2897:      * @return The list of range markers.
2898:      */
2899:     public Collection getRangeMarkers(Layer layer) {
2900:         return getRangeMarkers(0, layer);
2901:     }
2902: 
2903:     /**
2904:      * Returns a collection of domain markers for a particular renderer and
2905:      * layer.
2906:      *
2907:      * @param index  the renderer index.
2908:      * @param layer  the layer.
2909:      *
2910:      * @return A collection of markers (possibly <code>null</code>).
2911:      */
2912:     public Collection getDomainMarkers(int index, Layer layer) {
2913:         Collection result = null;
2914:         Integer key = new Integer(index);
2915:         if (layer == Layer.FOREGROUND) {
2916:             result = (Collection) this.foregroundDomainMarkers.get(key);
2917:         }
2918:         else if (layer == Layer.BACKGROUND) {
2919:             result = (Collection) this.backgroundDomainMarkers.get(key);
2920:         }
2921:         if (result != null) {
2922:             result = Collections.unmodifiableCollection(result);
2923:         }
2924:         return result;
2925:     }
2926: 
2927:     /**
2928:      * Returns a collection of range markers for a particular renderer and
2929:      * layer.
2930:      *
2931:      * @param index  the renderer index.
2932:      * @param layer  the layer.
2933:      *
2934:      * @return A collection of markers (possibly <code>null</code>).
2935:      */
2936:     public Collection getRangeMarkers(int index, Layer layer) {
2937:         Collection result = null;
2938:         Integer key = new Integer(index);
2939:         if (layer == Layer.FOREGROUND) {
2940:             result = (Collection) this.foregroundRangeMarkers.get(key);
2941:         }
2942:         else if (layer == Layer.BACKGROUND) {
2943:             result = (Collection) this.backgroundRangeMarkers.get(key);
2944:         }
2945:         if (result != null) {
2946:             result = Collections.unmodifiableCollection(result);
2947:         }
2948:         return result;
2949:     }
2950: 
2951:     /**
2952:      * Utility method for drawing a horizontal line across the data area of the
2953:      * plot.
2954:      *
2955:      * @param g2  the graphics device.
2956:      * @param dataArea  the data area.
2957:      * @param value  the coordinate, where to draw the line.
2958:      * @param stroke  the stroke to use.
2959:      * @param paint  the paint to use.
2960:      */
2961:     protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea,
2962:                                       double value, Stroke stroke,
2963:                                       Paint paint) {
2964: 
2965:         ValueAxis axis = getRangeAxis();
2966:         if (getOrientation() == PlotOrientation.HORIZONTAL) {
2967:             axis = getDomainAxis();
2968:         }
2969:         if (axis.getRange().contains(value)) {
2970:             double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT);
2971:             Line2D line = new Line2D.Double(
2972:                 dataArea.getMinX(), yy, dataArea.getMaxX(), yy
2973:             );
2974:             g2.setStroke(stroke);
2975:             g2.setPaint(paint);
2976:             g2.draw(line);
2977:         }
2978: 
2979:     }
2980: 
2981:     /**
2982:      * Utility method for drawing a vertical line on the data area of the plot.
2983:      *
2984:      * @param g2  the graphics device.
2985:      * @param dataArea  the data area.
2986:      * @param value  the coordinate, where to draw the line.
2987:      * @param stroke  the stroke to use.
2988:      * @param paint  the paint to use.
2989:      */
2990:     protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea,
2991:                                     double value, Stroke stroke, Paint paint) {
2992: 
2993:         ValueAxis axis = getDomainAxis();
2994:         if (getOrientation() == PlotOrientation.HORIZONTAL) {
2995:             axis = getRangeAxis();
2996:         }
2997:         if (axis.getRange().contains(value)) {
2998:             double xx = axis.valueToJava2D(
2999:                 value, dataArea, RectangleEdge.BOTTOM
3000:             );
3001:             Line2D line = new Line2D.Double(
3002:                 xx, dataArea.getMinY(), xx, dataArea.getMaxY()
3003:             );
3004:             g2.setStroke(stroke);
3005:             g2.setPaint(paint);
3006:             g2.draw(line);
3007:         }
3008: 
3009:     }
3010: 
3011:     /**
3012:      * Handles a 'click' on the plot by updating the anchor values...
3013:      *
3014:      * @param x  the x-coordinate, where the click occurred, in Java2D space.
3015:      * @param y  the y-coordinate, where the click occurred, in Java2D space.
3016:      * @param info  object containing information about the plot dimensions.
3017:      */
3018:     public void handleClick(int x, int y, PlotRenderingInfo info) {
3019: 
3020:         Rectangle2D dataArea = info.getDataArea();
3021:         if (dataArea.contains(x, y)) {
3022:             // set the anchor value for the horizontal axis...
3023:             ValueAxis da = getDomainAxis();
3024:             if (da != null) {
3025:                 double hvalue = da.java2DToValue(
3026:                     x, info.getDataArea(), getDomainAxisEdge()
3027:                 );
3028: 
3029:                 setDomainCrosshairValue(hvalue);
3030:             }
3031: 
3032:             // set the anchor value for the vertical axis...
3033:             ValueAxis ra = getRangeAxis();
3034:             if (ra != null) {
3035:                 double vvalue = ra.java2DToValue(
3036:                     y, info.getDataArea(), getRangeAxisEdge()
3037:                 );
3038:                 setRangeCrosshairValue(vvalue);
3039:             }
3040:         }
3041:     }
3042: 
3043:     /**
3044:      * A utility method that returns a list of datasets that are mapped to a
3045:      * particular axis.
3046:      *
3047:      * @param axisIndex  the axis index (<code>null</code> not permitted).
3048:      *
3049:      * @return A list of datasets.
3050:      */
3051:     private List getDatasetsMappedToDomainAxis(Integer axisIndex) {
3052:         if (axisIndex == null) {
3053:             throw new IllegalArgumentException("Null 'axisIndex' argument.");
3054:         }
3055:         List result = new ArrayList();
3056:         for (int i = 0; i < this.datasets.size(); i++) {
3057:             Integer mappedAxis = (Integer) this.datasetToDomainAxisMap.get(
3058:                 new Integer(i)
3059:             );
3060:             if (mappedAxis == null) {
3061:                 if (axisIndex.equals(ZERO)) {
3062:                     result.add(this.datasets.get(i));
3063:                 }
3064:             }
3065:             else {
3066:                 if (mappedAxis.equals(axisIndex)) {
3067:                     result.add(this.datasets.get(i));
3068:                 }
3069:             }
3070:         }
3071:         return result;
3072:     }
3073: 
3074:     /**
3075:      * A utility method that returns a list of datasets that are mapped to a
3076:      * particular axis.
3077:      *
3078:      * @param axisIndex  the axis index (<code>null</code> not permitted).
3079:      *
3080:      * @return A list of datasets.
3081:      */
3082:     private List getDatasetsMappedToRangeAxis(Integer axisIndex) {
3083:         if (axisIndex == null) {
3084:             throw new IllegalArgumentException("Null 'axisIndex' argument.");
3085:         }
3086:         List result = new ArrayList();
3087:         for (int i = 0; i < this.datasets.size(); i++) {
3088:             Integer mappedAxis = (Integer) this.datasetToRangeAxisMap.get(
3089:                 new Integer(i)
3090:             );
3091:             if (mappedAxis == null) {
3092:                 if (axisIndex.equals(ZERO)) {
3093:                     result.add(this.datasets.get(i));
3094:                 }
3095:             }
3096:             else {
3097:                 if (mappedAxis.equals(axisIndex)) {
3098:                     result.add(this.datasets.get(i));
3099:                 }
3100:             }
3101:         }
3102:         return result;
3103:     }
3104: 
3105:     /**
3106:      * Returns the index of the given domain axis.
3107:      *
3108:      * @param axis  the axis.
3109:      *
3110:      * @return The axis index.
3111:      */
3112:     protected int getDomainAxisIndex(ValueAxis axis) {
3113:         int result = this.domainAxes.indexOf(axis);
3114:         if (result < 0) {
3115:             // try the parent plot
3116:             Plot parent = getParent();
3117:             if (parent instanceof XYPlot) {
3118:                 XYPlot p = (XYPlot) parent;
3119:                 result = p.getDomainAxisIndex(axis);
3120:             }
3121:         }
3122:         return result;
3123:     }
3124: 
3125:     /**
3126:      * Returns the index of the given range axis.
3127:      *
3128:      * @param axis  the axis.
3129:      *
3130:      * @return The axis index.
3131:      */
3132:     protected int getRangeAxisIndex(ValueAxis axis) {
3133:         int result = this.rangeAxes.indexOf(axis);
3134:         if (result < 0) {
3135:             // try the parent plot
3136:             Plot parent = getParent();
3137:             if (parent instanceof XYPlot) {
3138:                 XYPlot p = (XYPlot) parent;
3139:                 result = p.getRangeAxisIndex(axis);
3140:             }
3141:         }
3142:         return result;
3143:     }
3144: 
3145:     /**
3146:      * Returns the range for the specified axis.
3147:      *
3148:      * @param axis  the axis.
3149:      *
3150:      * @return The range.
3151:      */
3152:     public Range getDataRange(ValueAxis axis) {
3153: 
3154:         Range result = null;
3155:         List mappedDatasets = new ArrayList();
3156:         boolean isDomainAxis = true;
3157: 
3158:         // is it a domain axis?
3159:         int domainIndex = getDomainAxisIndex(axis);
3160:         if (domainIndex >= 0) {
3161:             isDomainAxis = true;
3162:             mappedDatasets.addAll(
3163:                 getDatasetsMappedToDomainAxis(new Integer(domainIndex))
3164:             );
3165:         }
3166: 
3167:         // or is it a range axis?
3168:         int rangeIndex = getRangeAxisIndex(axis);
3169:         if (rangeIndex >= 0) {
3170:             isDomainAxis = false;
3171:             mappedDatasets.addAll(
3172:                 getDatasetsMappedToRangeAxis(new Integer(rangeIndex))
3173:             );
3174:         }
3175: 
3176:         // iterate through the datasets that map to the axis and get the union
3177:         // of the ranges.
3178:         Iterator iterator = mappedDatasets.iterator();
3179:         while (iterator.hasNext()) {
3180:             XYDataset d = (XYDataset) iterator.next();
3181:             if (d != null) {
3182:                 XYItemRenderer r = getRendererForDataset(d);
3183:                 if (isDomainAxis) {
3184:                     if (r != null) {
3185:                         result = Range.combine(result, r.findDomainBounds(d));
3186:                     }
3187:                     else {
3188:                         result = Range.combine(
3189:                             result, DatasetUtilities.findDomainBounds(d)
3190:                         );
3191:                     }
3192:                 }
3193:                 else {
3194:                     if (r != null) {
3195:                         result = Range.combine(result, r.findRangeBounds(d));
3196:                     }
3197:                     else {
3198:                         result = Range.combine(
3199:                             result, DatasetUtilities.findRangeBounds(d)
3200:                         );
3201:                     }
3202:                 }
3203:             }
3204:         }
3205:         return result;
3206: 
3207:     }
3208: 
3209:     /**
3210:      * Receives notification of a change to the plot's dataset.
3211:      * <P>
3212:      * The axis ranges are updated if necessary.
3213:      *
3214:      * @param event  information about the event (not used here).
3215:      */
3216:     public void datasetChanged(DatasetChangeEvent event) {
3217:         configureDomainAxes();
3218:         configureRangeAxes();
3219:         if (getParent() != null) {
3220:             getParent().datasetChanged(event);
3221:         }
3222:         else {
3223:             PlotChangeEvent e = new PlotChangeEvent(this);
3224:             e.setType(ChartChangeEventType.DATASET_UPDATED);
3225:             notifyListeners(e);
3226:         }
3227:     }
3228: 
3229:     /**
3230:      * Receives notification of a renderer change event.
3231:      *
3232:      * @param event  the event.
3233:      */
3234:     public void rendererChanged(RendererChangeEvent event) {
3235:         notifyListeners(new PlotChangeEvent(this));
3236:     }
3237: 
3238:     /**
3239:      * Returns a flag indicating whether or not the domain crosshair is visible.
3240:      *
3241:      * @return The flag.
3242:      */
3243:     public boolean isDomainCrosshairVisible() {
3244:         return this.domainCrosshairVisible;
3245:     }
3246: 
3247:     /**
3248:      * Sets the flag indicating whether or not the domain crosshair is visible.
3249:      *
3250:      * @param flag  the new value of the flag.
3251:      */
3252:     public void setDomainCrosshairVisible(boolean flag) {
3253: 
3254:         if (this.domainCrosshairVisible != flag) {
3255:             this.domainCrosshairVisible = flag;
3256:             notifyListeners(new PlotChangeEvent(this));
3257:         }
3258: 
3259:     }
3260: 
3261:     /**
3262:      * Returns a flag indicating whether or not the crosshair should "lock-on"
3263:      * to actual data values.
3264:      *
3265:      * @return The flag.
3266:      */
3267:     public boolean isDomainCrosshairLockedOnData() {
3268:         return this.domainCrosshairLockedOnData;
3269:     }
3270: 
3271:     /**
3272:      * Sets the flag indicating whether or not the domain crosshair should
3273:      * "lock-on" to actual data values.
3274:      *
3275:      * @param flag  the flag.
3276:      */
3277:     public void setDomainCrosshairLockedOnData(boolean flag) {
3278: 
3279:         if (this.domainCrosshairLockedOnData != flag) {
3280:             this.domainCrosshairLockedOnData = flag;
3281:             notifyListeners(new PlotChangeEvent(this));
3282:         }
3283: 
3284:     }
3285: 
3286:     /**
3287:      * Returns the domain crosshair value.
3288:      *
3289:      * @return The value.
3290:      */
3291:     public double getDomainCrosshairValue() {
3292:         return this.domainCrosshairValue;
3293:     }
3294: 
3295:     /**
3296:      * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to
3297:      * all registered listeners (provided that the domain crosshair is visible).
3298:      *
3299:      * @param value  the value.
3300:      */
3301:     public void setDomainCrosshairValue(double value) {
3302:         setDomainCrosshairValue(value, true);
3303:     }
3304: 
3305:     /**
3306:      * Sets the domain crosshair value and, if requested, sends a
3307:      * {@link PlotChangeEvent} to all registered listeners (provided that the
3308:      * domain crosshair is visible).
3309:      *
3310:      * @param value  the new value.
3311:      * @param notify  notify listeners?
3312:      */
3313:     public void setDomainCrosshairValue(double value, boolean notify) {
3314:         this.domainCrosshairValue = value;
3315:         if (isDomainCrosshairVisible() && notify) {
3316:             notifyListeners(new PlotChangeEvent(this));
3317:         }
3318:     }
3319: 
3320:     /**
3321:      * Returns the {@link Stroke} used to draw the crosshair (if visible).
3322:      *
3323:      * @return The crosshair stroke.
3324:      */
3325:     public Stroke getDomainCrosshairStroke() {
3326:         return this.domainCrosshairStroke;
3327:     }
3328: 
3329:     /**
3330:      * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3331:      * registered listeners that the axis has been modified.
3332:      *
3333:      * @param stroke  the new crosshair stroke.
3334:      */
3335:     public void setDomainCrosshairStroke(Stroke stroke) {
3336:         this.domainCrosshairStroke = stroke;
3337:         notifyListeners(new PlotChangeEvent(this));
3338:     }
3339: 
3340:     /**
3341:      * Returns the domain crosshair color.
3342:      *
3343:      * @return The crosshair color.
3344:      */
3345:     public Paint getDomainCrosshairPaint() {
3346:         return this.domainCrosshairPaint;
3347:     }
3348: 
3349:     /**
3350:      * Sets the Paint used to color the crosshairs (if visible) and notifies
3351:      * registered listeners that the axis has been modified.
3352:      *
3353:      * @param paint the new crosshair paint.
3354:      */
3355:     public void setDomainCrosshairPaint(Paint paint) {
3356:         this.domainCrosshairPaint = paint;
3357:         notifyListeners(new PlotChangeEvent(this));
3358:     }
3359: 
3360:     /**
3361:      * Returns a flag indicating whether or not the range crosshair is visible.
3362:      *
3363:      * @return The flag.
3364:      */
3365:     public boolean isRangeCrosshairVisible() {
3366:         return this.rangeCrosshairVisible;
3367:     }
3368: 
3369:     /**
3370:      * Sets the flag indicating whether or not the range crosshair is visible.
3371:      *
3372:      * @param flag  the new value of the flag.
3373:      */
3374:     public void setRangeCrosshairVisible(boolean flag) {
3375: 
3376:         if (this.rangeCrosshairVisible != flag) {
3377:             this.rangeCrosshairVisible = flag;
3378:             notifyListeners(new PlotChangeEvent(this));
3379:         }
3380: 
3381:     }
3382: 
3383:     /**
3384:      * Returns a flag indicating whether or not the crosshair should "lock-on"
3385:      * to actual data values.
3386:      *
3387:      * @return The flag.
3388:      */
3389:     public boolean isRangeCrosshairLockedOnData() {
3390:         return this.rangeCrosshairLockedOnData;
3391:     }
3392: 
3393:     /**
3394:      * Sets the flag indicating whether or not the range crosshair should
3395:      * "lock-on" to actual data values.
3396:      *
3397:      * @param flag  the flag.
3398:      */
3399:     public void setRangeCrosshairLockedOnData(boolean flag) {
3400: 
3401:         if (this.rangeCrosshairLockedOnData != flag) {
3402:             this.rangeCrosshairLockedOnData = flag;
3403:             notifyListeners(new PlotChangeEvent(this));
3404:         }
3405: 
3406:     }
3407: 
3408:     /**
3409:      * Returns the range crosshair value.
3410:      *
3411:      * @return The value.
3412:      */
3413:     public double getRangeCrosshairValue() {
3414:         return this.rangeCrosshairValue;
3415:     }
3416: 
3417:     /**
3418:      * Sets the domain crosshair value.
3419:      * <P>
3420:      * Registered listeners are notified that the plot has been modified, but
3421:      * only if the crosshair is visible.
3422:      *
3423:      * @param value  the new value.
3424:      */
3425:     public void setRangeCrosshairValue(double value) {
3426:         setRangeCrosshairValue(value, true);
3427:     }
3428: 
3429:     /**
3430:      * Sets the range crosshair value.
3431:      * <P>
3432:      * Registered listeners are notified that the axis has been modified, but
3433:      * only if the crosshair is visible.
3434:      *
3435:      * @param value  the new value.
3436:      * @param notify  a flag that controls whether or not listeners are
3437:      *                notified.
3438:      */
3439:     public void setRangeCrosshairValue(double value, boolean notify) {
3440:         this.rangeCrosshairValue = value;
3441:         if (isRangeCrosshairVisible() && notify) {
3442:             notifyListeners(new PlotChangeEvent(this));
3443:         }
3444:     }
3445: 
3446:     /**
3447:      * Returns the Stroke used to draw the crosshair (if visible).
3448:      *
3449:      * @return The crosshair stroke.
3450:      */
3451:     public Stroke getRangeCrosshairStroke() {
3452:         return this.rangeCrosshairStroke;
3453:     }
3454: 
3455:     /**
3456:      * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3457:      * registered listeners that the axis has been modified.
3458:      *
3459:      * @param stroke  the new crosshair stroke.
3460:      */
3461:     public void setRangeCrosshairStroke(Stroke stroke) {
3462:         this.rangeCrosshairStroke = stroke;
3463:         notifyListeners(new PlotChangeEvent(this));
3464:     }
3465: 
3466:     /**
3467:      * Returns the range crosshair color.
3468:      *
3469:      * @return The crosshair color.
3470:      */
3471:     public Paint getRangeCrosshairPaint() {
3472:         return this.rangeCrosshairPaint;
3473:     }
3474: 
3475:     /**
3476:      * Sets the Paint used to color the crosshairs (if visible) and notifies
3477:      * registered listeners that the axis has been modified.
3478:      *
3479:      * @param paint the new crosshair paint.
3480:      */
3481:     public void setRangeCrosshairPaint(Paint paint) {
3482:         this.rangeCrosshairPaint = paint;
3483:         notifyListeners(new PlotChangeEvent(this));
3484:     }
3485: 
3486:     /**
3487:      * Returns the fixed domain axis space.
3488:      *
3489:      * @return The fixed domain axis space (possibly <code>null</code>).
3490:      */
3491:     public AxisSpace getFixedDomainAxisSpace() {
3492:         return this.fixedDomainAxisSpace;
3493:     }
3494: 
3495:     /**
3496:      * Sets the fixed domain axis space.
3497:      *
3498:      * @param space  the space.
3499:      */
3500:     public void setFixedDomainAxisSpace(AxisSpace space) {
3501:         this.fixedDomainAxisSpace = space;
3502:     }
3503: 
3504:     /**
3505:      * Returns the fixed range axis space.
3506:      *
3507:      * @return The fixed range axis space.
3508:      */
3509:     public AxisSpace getFixedRangeAxisSpace() {
3510:         return this.fixedRangeAxisSpace;
3511:     }
3512: 
3513:     /**
3514:      * Sets the fixed range axis space.
3515:      *
3516:      * @param space  the space.
3517:      */
3518:     public void setFixedRangeAxisSpace(AxisSpace space) {
3519:         this.fixedRangeAxisSpace = space;
3520:     }
3521: 
3522:     /**
3523:      * Multiplies the range on the domain axis/axes by the specified factor.
3524:      *
3525:      * @param factor  the zoom factor.
3526:      * @param info  the plot rendering info.
3527:      * @param source  the source point.
3528:      */
3529:     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
3530:                                Point2D source) {
3531:         for (int i = 0; i < this.domainAxes.size(); i++) {
3532:             ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
3533:             if (domainAxis != null) {
3534:                 domainAxis.resizeRange(factor);
3535:             }
3536:         }
3537:     }
3538: 
3539:     /**
3540:      * Zooms in on the domain axis/axes.  The new lower and upper bounds are
3541:      * specified as percentages of the current axis range, where 0 percent is
3542:      * the current lower bound and 100 percent is the current upper bound.
3543:      *
3544:      * @param lowerPercent  a percentage that determines the new lower bound
3545:      *                      for the axis (e.g. 0.20 is twenty percent).
3546:      * @param upperPercent  a percentage that determines the new upper bound
3547:      *                      for the axis (e.g. 0.80 is eighty percent).
3548:      * @param info  the plot rendering info.
3549:      * @param source  the source point.
3550:      */
3551:     public void zoomDomainAxes(double lowerPercent, double upperPercent,
3552:                                PlotRenderingInfo info, Point2D source) {
3553:         for (int i = 0; i < this.domainAxes.size(); i++) {
3554:             ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
3555:             if (domainAxis != null) {
3556:                 domainAxis.zoomRange(lowerPercent, upperPercent);
3557:             }
3558:         }
3559:     }
3560: 
3561:     /**
3562:      * Multiplies the range on the range axis/axes by the specified factor.
3563:      *
3564:      * @param factor  the zoom factor.
3565:      * @param info  the plot rendering info.
3566:      * @param source  the source point.
3567:      */
3568:     public void zoomRangeAxes(double factor, PlotRenderingInfo info,
3569:                               Point2D source) {
3570:         for (int i = 0; i < this.rangeAxes.size(); i++) {
3571:             ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
3572:             if (rangeAxis != null) {
3573:                 rangeAxis.resizeRange(factor);
3574:             }
3575:         }
3576:     }
3577: 
3578:     /**
3579:      * Zooms in on the range axes.
3580:      *
3581:      * @param lowerPercent  the lower bound.
3582:      * @param upperPercent  the upper bound.
3583:      * @param info  the plot rendering info.
3584:      * @param source  the source point.
3585:      */
3586:     public void zoomRangeAxes(double lowerPercent, double upperPercent,
3587:                               PlotRenderingInfo info, Point2D source) {
3588:         for (int i = 0; i < this.rangeAxes.size(); i++) {
3589:             ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
3590:             if (rangeAxis != null) {
3591:                 rangeAxis.zoomRange(lowerPercent, upperPercent);
3592:             }
3593:         }
3594:     }
3595: 
3596:     /**
3597:      * Returns <code>true</code>
3598:      *
3599:      * @return A boolean.
3600:      */
3601:     public boolean isDomainZoomable() {
3602:         return true;
3603:     }
3604: 
3605:     /**
3606:      * Returns <code>true</code>
3607:      *
3608:      * @return A boolean.
3609:      */
3610:     public boolean isRangeZoomable() {
3611:         return true;
3612:     }
3613: 
3614:     /**
3615:      * Returns the number of series in the primary dataset for this plot.  If
3616:      * the dataset is <code>null</code>, the method returns 0.
3617:      *
3618:      * @return The series count.
3619:      */
3620:     public int getSeriesCount() {
3621:         int result = 0;
3622:         XYDataset dataset = getDataset();
3623:         if (dataset != null) {
3624:             result = dataset.getSeriesCount();
3625:         }
3626:         return result;
3627:     }
3628: 
3629:     /**
3630:      * Returns the fixed legend items, if any.
3631:      *
3632:      * @return The legend items (possibly <code>null</code>).
3633:      */
3634:     public LegendItemCollection getFixedLegendItems() {
3635:         return this.fixedLegendItems;
3636:     }
3637: 
3638:     /**
3639:      * Sets the fixed legend items for the plot.  Leave this set to
3640:      * <code>null</code> if you prefer the legend items to be created
3641:      * automatically.
3642:      *
3643:      * @param items  the legend items (<code>null</code> permitted).
3644:      */
3645:     public void setFixedLegendItems(LegendItemCollection items) {
3646:         this.fixedLegendItems = items;
3647:         notifyListeners(new PlotChangeEvent(this));
3648:     }
3649: 
3650:     /**
3651:      * Returns the legend items for the plot.  Each legend item is generated by
3652:      * the plot's renderer, since the renderer is responsible for the visual
3653:      * representation of the data.
3654:      *
3655:      * @return The legend items.
3656:      */
3657:     public LegendItemCollection getLegendItems() {
3658:         if (this.fixedLegendItems != null) {
3659:             return this.fixedLegendItems;
3660:         }
3661:         LegendItemCollection result = new LegendItemCollection();
3662:         int count = this.datasets.size();
3663:         for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
3664:             XYDataset dataset = getDataset(datasetIndex);
3665:             if (dataset != null) {
3666:                 XYItemRenderer renderer = getRenderer(datasetIndex);
3667:                 if (renderer == null) {
3668:                     renderer = getRenderer(0);
3669:                 }
3670:                 if (renderer != null) {
3671:                     int seriesCount = dataset.getSeriesCount();
3672:                     for (int i = 0; i < seriesCount; i++) {
3673:                         if (renderer.isSeriesVisible(i)
3674:                                 && renderer.isSeriesVisibleInLegend(i)) {
3675:                             LegendItem item = renderer.getLegendItem(
3676:                                 datasetIndex, i
3677:                             );
3678:                             if (item != null) {
3679:                                 result.add(item);
3680:                             }
3681:                         }
3682:                     }
3683:                 }
3684:             }
3685:         }
3686:         return result;
3687:     }
3688: 
3689:     /**
3690:      * Tests this plot for equality with another object.
3691:      *
3692:      * @param obj  the object (<code>null</code> permitted).
3693:      *
3694:      * @return <code>true</code> or <code>false</code>.
3695:      */
3696:     public boolean equals(Object obj) {
3697: 
3698:         if (obj == this) {
3699:             return true;
3700:         }
3701:         if (!(obj instanceof XYPlot)) {
3702:             return false;
3703:         }
3704:         if (!super.equals(obj))  {
3705:             return false;
3706:         }
3707: 
3708:         XYPlot that = (XYPlot) obj;
3709:         if (this.weight != that.weight) {
3710:             return false;
3711:         }
3712:         if (this.orientation != that.orientation) {
3713:             return false;
3714:         }
3715:         if (!this.domainAxes.equals(that.domainAxes)) {
3716:             return false;
3717:         }
3718:         if (!this.domainAxisLocations.equals(that.domainAxisLocations)) {
3719:             return false;
3720:         }
3721:         if (this.rangeCrosshairLockedOnData
3722:                 != that.rangeCrosshairLockedOnData) {
3723:             return false;
3724:         }
3725:         if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
3726:             return false;
3727:         }
3728:         if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) {
3729:             return false;
3730:         }
3731:         if (this.rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) {
3732:             return false;
3733:         }
3734:         if (this.domainCrosshairVisible != that.domainCrosshairVisible) {
3735:             return false;
3736:         }
3737:         if (this.domainCrosshairValue != that.domainCrosshairValue) {
3738:             return false;
3739:         }
3740:         if (this.domainCrosshairLockedOnData
3741:                 != that.domainCrosshairLockedOnData) {
3742:             return false;
3743:         }
3744:         if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) {
3745:             return false;
3746:         }
3747:         if (this.rangeCrosshairValue != that.rangeCrosshairValue) {
3748:             return false;
3749:         }
3750:         if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) {
3751:             return false;
3752:         }
3753:         if (!ObjectUtilities.equal(this.renderers, that.renderers)) {
3754:             return false;
3755:         }
3756:         if (!ObjectUtilities.equal(this.rangeAxes, that.rangeAxes)) {
3757:             return false;
3758:         }
3759:         if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) {
3760:             return false;
3761:         }
3762:         if (!ObjectUtilities.equal(
3763:                 this.datasetToDomainAxisMap, that.datasetToDomainAxisMap
3764:             )) {
3765:             return false;
3766:         }
3767:         if (!ObjectUtilities.equal(
3768:                 this.datasetToRangeAxisMap, that.datasetToRangeAxisMap
3769:             )) {
3770:             return false;
3771:         }
3772:         if (!ObjectUtilities.equal(
3773:                 this.domainGridlineStroke, that.domainGridlineStroke)) {
3774:             return false;
3775:         }
3776:         if (!PaintUtilities.equal(
3777:                 this.domainGridlinePaint, that.domainGridlinePaint)) {
3778:             return false;
3779:         }
3780:         if (!ObjectUtilities.equal(
3781:                 this.rangeGridlineStroke, that.rangeGridlineStroke)) {
3782:             return false;
3783:         }
3784:         if (!PaintUtilities.equal(
3785:                 this.rangeGridlinePaint, that.rangeGridlinePaint)) {
3786:             return false;
3787:         }
3788:         if (!PaintUtilities.equal(
3789:                 this.rangeZeroBaselinePaint, that.rangeZeroBaselinePaint
3790:             )) {
3791:             return false;
3792:         }
3793:         if (!ObjectUtilities.equal(
3794:                 this.rangeZeroBaselineStroke, that.rangeZeroBaselineStroke
3795:             )) {
3796:             return false;
3797:         }
3798:         if (!ObjectUtilities.equal(
3799:                 this.domainCrosshairStroke, that.domainCrosshairStroke
3800:             )) {
3801:             return false;
3802:         }
3803:         if (!PaintUtilities.equal(
3804:                 this.domainCrosshairPaint, that.domainCrosshairPaint
3805:             )) {
3806:             return false;
3807:         }
3808:         if (!ObjectUtilities.equal(
3809:                 this.rangeCrosshairStroke, that.rangeCrosshairStroke
3810:             )) {
3811:             return false;
3812:         }
3813:         if (!PaintUtilities.equal(
3814:                 this.rangeCrosshairPaint, that.rangeCrosshairPaint
3815:             )) {
3816:             return false;
3817:         }
3818:         if (!ObjectUtilities.equal(
3819:                 this.foregroundDomainMarkers, that.foregroundDomainMarkers
3820:             )) {
3821:             return false;
3822:         }
3823:         if (!ObjectUtilities.equal(
3824:                 this.backgroundDomainMarkers, that.backgroundDomainMarkers
3825:             )) {
3826:             return false;
3827:         }
3828:         if (!ObjectUtilities.equal(
3829:                 this.foregroundRangeMarkers, that.foregroundRangeMarkers
3830:             )) {
3831:             return false;
3832:         }
3833:         if (!ObjectUtilities.equal(
3834:                 this.backgroundRangeMarkers, that.backgroundRangeMarkers
3835:             )) {
3836:             return false;
3837:         }
3838:         if (!ObjectUtilities.equal(
3839:                 this.foregroundDomainMarkers, that.foregroundDomainMarkers
3840:             )) {
3841:             return false;
3842:         }
3843:         if (!ObjectUtilities.equal(
3844:                 this.backgroundDomainMarkers, that.backgroundDomainMarkers
3845:             )) {
3846:             return false;
3847:         }
3848:         if (!ObjectUtilities.equal(
3849:                 this.foregroundRangeMarkers, that.foregroundRangeMarkers
3850:             )) {
3851:             return false;
3852:         }
3853:         if (!ObjectUtilities.equal(
3854:                 this.backgroundRangeMarkers, that.backgroundRangeMarkers
3855:             )) {
3856:             return false;
3857:         }
3858:         if (!ObjectUtilities.equal(
3859:                 this.annotations, that.annotations
3860:             )) {
3861:             return false;
3862:         }
3863:         return true;
3864: 
3865:     }
3866: 
3867:     /**
3868:      * Returns a clone of the plot.
3869:      *
3870:      * @return A clone.
3871:      *
3872:      * @throws CloneNotSupportedException  this can occur if some component of
3873:      *         the plot cannot be cloned.
3874:      */
3875:     public Object clone() throws CloneNotSupportedException {
3876: 
3877:         XYPlot clone = (XYPlot) super.clone();
3878:         clone.domainAxes = (ObjectList) ObjectUtilities.clone(this.domainAxes);
3879:         for (int i = 0; i < this.domainAxes.size(); i++) {
3880:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
3881:             if (axis != null) {
3882:                 ValueAxis clonedAxis = (ValueAxis) axis.clone();
3883:                 clone.domainAxes.set(i, clonedAxis);
3884:                 clonedAxis.setPlot(clone);
3885:                 clonedAxis.addChangeListener(clone);
3886:             }
3887:         }
3888:         clone.domainAxisLocations
3889:             = (ObjectList) this.domainAxisLocations.clone();
3890: 
3891:         clone.rangeAxes = (ObjectList) ObjectUtilities.clone(this.rangeAxes);
3892:         for (int i = 0; i < this.rangeAxes.size(); i++) {
3893:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
3894:             if (axis != null) {
3895:                 ValueAxis clonedAxis = (ValueAxis) axis.clone();
3896:                 clone.rangeAxes.set(i, clonedAxis);
3897:                 clonedAxis.setPlot(clone);
3898:                 clonedAxis.addChangeListener(clone);
3899:             }
3900:         }
3901:         clone.rangeAxisLocations
3902:             = (ObjectList) ObjectUtilities.clone(this.rangeAxisLocations);
3903: 
3904:         // the datasets are not cloned, but listeners need to be added...
3905:         clone.datasets = (ObjectList) ObjectUtilities.clone(this.datasets);
3906:         for (int i = 0; i < clone.datasets.size(); ++i) {
3907:             XYDataset d = getDataset(i);
3908:             if (d != null) {
3909:                 d.addChangeListener(clone);
3910:             }
3911:         }
3912: 
3913:         clone.datasetToDomainAxisMap = new TreeMap();
3914:         clone.datasetToDomainAxisMap.putAll(this.datasetToDomainAxisMap);
3915:         clone.datasetToRangeAxisMap = new TreeMap();
3916:         clone.datasetToRangeAxisMap.putAll(this.datasetToRangeAxisMap);
3917: 
3918:         clone.renderers = (ObjectList) ObjectUtilities.clone(this.renderers);
3919:         for (int i = 0; i < this.renderers.size(); i++) {
3920:             XYItemRenderer renderer2 = (XYItemRenderer) this.renderers.get(i);
3921:             if (renderer2 instanceof PublicCloneable) {
3922:                 PublicCloneable pc = (PublicCloneable) renderer2;
3923:                 clone.renderers.set(i, pc.clone());
3924:             }
3925:         }
3926:         clone.foregroundDomainMarkers = (Map) ObjectUtilities.clone(
3927:             this.foregroundDomainMarkers
3928:         );
3929:         clone.backgroundDomainMarkers = (Map) ObjectUtilities.clone(
3930:             this.backgroundDomainMarkers
3931:         );
3932:         clone.foregroundRangeMarkers = (Map) ObjectUtilities.clone(
3933:             this.foregroundRangeMarkers
3934:         );
3935:         clone.backgroundRangeMarkers = (Map) ObjectUtilities.clone(
3936:             this.backgroundRangeMarkers
3937:         );
3938:         clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
3939:         if (this.fixedDomainAxisSpace != null) {
3940:             clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone(
3941:                 this.fixedDomainAxisSpace
3942:             );
3943:         }
3944:         if (this.fixedRangeAxisSpace != null) {
3945:             clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone(
3946:                 this.fixedRangeAxisSpace
3947:             );
3948:         }
3949:         return clone;
3950: 
3951:     }
3952: 
3953:     /**
3954:      * Provides serialization support.
3955:      *
3956:      * @param stream  the output stream.
3957:      *
3958:      * @throws IOException  if there is an I/O error.
3959:      */
3960:     private void writeObject(ObjectOutputStream stream) throws IOException {
3961:         stream.defaultWriteObject();
3962:         SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
3963:         SerialUtilities.writePaint(this.domainGridlinePaint, stream);
3964:         SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
3965:         SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
3966:         SerialUtilities.writeStroke(this.rangeZeroBaselineStroke, stream);
3967:         SerialUtilities.writePaint(this.rangeZeroBaselinePaint, stream);
3968:         SerialUtilities.writeStroke(this.domainCrosshairStroke, stream);
3969:         SerialUtilities.writePaint(this.domainCrosshairPaint, stream);
3970:         SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream);
3971:         SerialUtilities.writePaint(this.rangeCrosshairPaint, stream);
3972:         SerialUtilities.writePaint(this.domainTickBandPaint, stream);
3973:         SerialUtilities.writePaint(this.rangeTickBandPaint, stream);
3974:         SerialUtilities.writePoint2D(this.quadrantOrigin, stream);
3975:         for (int i = 0; i < 4; i++) {
3976:             SerialUtilities.writePaint(this.quadrantPaint[i], stream);
3977:         }
3978:     }
3979: 
3980:     /**
3981:      * Provides serialization support.
3982:      *
3983:      * @param stream  the input stream.
3984:      *
3985:      * @throws IOException  if there is an I/O error.
3986:      * @throws ClassNotFoundException  if there is a classpath problem.
3987:      */
3988:     private void readObject(ObjectInputStream stream)
3989:         throws IOException, ClassNotFoundException {
3990: 
3991:         stream.defaultReadObject();
3992:         this.domainGridlineStroke = SerialUtilities.readStroke(stream);
3993:         this.domainGridlinePaint = SerialUtilities.readPaint(stream);
3994:         this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
3995:         this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
3996:         this.rangeZeroBaselineStroke = SerialUtilities.readStroke(stream);
3997:         this.rangeZeroBaselinePaint = SerialUtilities.readPaint(stream);
3998:         this.domainCrosshairStroke = SerialUtilities.readStroke(stream);
3999:         this.domainCrosshairPaint = SerialUtilities.readPaint(stream);
4000:         this.rangeCrosshairStroke = SerialUtilities.readStroke(stream);
4001:         this.rangeCrosshairPaint = SerialUtilities.readPaint(stream);
4002:         this.domainTickBandPaint = SerialUtilities.readPaint(stream);
4003:         this.rangeTickBandPaint = SerialUtilities.readPaint(stream);
4004:         this.quadrantOrigin = SerialUtilities.readPoint2D(stream);
4005:         this.quadrantPaint = new Paint[4];
4006:         for (int i = 0; i < 4; i++) {
4007:             this.quadrantPaint[i] = SerialUtilities.readPaint(stream);
4008:         }
4009: 
4010:         // register the plot as a listener with its axes, datasets, and 
4011:         // renderers...
4012:         int domainAxisCount = this.domainAxes.size();
4013:         for (int i = 0; i < domainAxisCount; i++) {
4014:             Axis axis = (Axis) this.domainAxes.get(i);
4015:             if (axis != null) {
4016:                 axis.setPlot(this);
4017:                 axis.addChangeListener(this);
4018:             }
4019:         }
4020:         int rangeAxisCount = this.rangeAxes.size();
4021:         for (int i = 0; i < rangeAxisCount; i++) {
4022:             Axis axis = (Axis) this.rangeAxes.get(i);
4023:             if (axis != null) {
4024:                 axis.setPlot(this);
4025:                 axis.addChangeListener(this);
4026:             }
4027:         }
4028:         int datasetCount = this.datasets.size();
4029:         for (int i = 0; i < datasetCount; i++) {
4030:             Dataset dataset = (Dataset) this.datasets.get(i);
4031:             if (dataset != null) {
4032:                 dataset.addChangeListener(this);
4033:             }
4034:         }
4035:         int rendererCount = this.renderers.size();
4036:         for (int i = 0; i < rendererCount; i++) {
4037:             XYItemRenderer renderer = (XYItemRenderer) this.renderers.get(i);
4038:             if (renderer != null) {
4039:                 renderer.addChangeListener(this);
4040:             }
4041:         }
4042:     
4043:     }
4044: 
4045: }