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-2008, 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-2008, 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:  *                   Sergei Ivanov;
  44:  *                   Richard West, Advanced Micro Devices, Inc.;
  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.x ---------------------------------------------
 170:  * 26-Jan-2006 : Added getAnnotations() method (DG);
 171:  * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
 172:  * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report 
 173:  *               1565168 (DG);
 174:  * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus 
 175:  *               API doc updates (DG);
 176:  * 29-Nov-2006 : Added argument checks (DG);
 177:  * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG);
 178:  * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG);
 179:  * 26-Feb-2007 : Added missing setDomainAxisLocation() and 
 180:  *               setRangeAxisLocation() methods (DG);
 181:  * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation
 182:  *               (see patch 1671648 by Sergei Ivanov) (DG);
 183:  * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG);
 184:  * 23-Mar-2007 : Added domain zero base line facility (DG);
 185:  * 04-May-2007 : Render only visible data items if possible (DG);
 186:  * 24-May-2007 : Fixed bug in render method for an empty series (DG);
 187:  * 07-Jun-2007 : Modified drawBackground() to pass orientation to 
 188:  *               fillBackground() for handling GradientPaint (DG);
 189:  * 24-Sep-2007 : Added new zoom methods (DG);
 190:  * 26-Sep-2007 : Include index value in IllegalArgumentExceptions (DG);
 191:  * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
 192:  *               and range markers (DG);
 193:  * 12-Nov-2007 : Fixed bug in equals() method for domain and range tick
 194:  *               band paint attributes (DG);
 195:  * 27-Nov-2007 : Added new setFixedDomain/RangeAxisSpace() methods (DG);
 196:  * 04-Jan-2008 : Fix for quadrant painting error - see patch 1849564 (DG);
 197:  * 
 198:  */
 199: 
 200: package org.jfree.chart.plot;
 201: 
 202: import java.awt.AlphaComposite;
 203: import java.awt.BasicStroke;
 204: import java.awt.Color;
 205: import java.awt.Composite;
 206: import java.awt.Graphics2D;
 207: import java.awt.Paint;
 208: import java.awt.Shape;
 209: import java.awt.Stroke;
 210: import java.awt.geom.Line2D;
 211: import java.awt.geom.Point2D;
 212: import java.awt.geom.Rectangle2D;
 213: import java.io.IOException;
 214: import java.io.ObjectInputStream;
 215: import java.io.ObjectOutputStream;
 216: import java.io.Serializable;
 217: import java.util.ArrayList;
 218: import java.util.Collection;
 219: import java.util.Collections;
 220: import java.util.HashMap;
 221: import java.util.Iterator;
 222: import java.util.List;
 223: import java.util.Map;
 224: import java.util.ResourceBundle;
 225: import java.util.Set;
 226: import java.util.TreeMap;
 227: 
 228: import org.jfree.chart.LegendItem;
 229: import org.jfree.chart.LegendItemCollection;
 230: import org.jfree.chart.annotations.XYAnnotation;
 231: import org.jfree.chart.axis.Axis;
 232: import org.jfree.chart.axis.AxisCollection;
 233: import org.jfree.chart.axis.AxisLocation;
 234: import org.jfree.chart.axis.AxisSpace;
 235: import org.jfree.chart.axis.AxisState;
 236: import org.jfree.chart.axis.ValueAxis;
 237: import org.jfree.chart.axis.ValueTick;
 238: import org.jfree.chart.event.ChartChangeEventType;
 239: import org.jfree.chart.event.PlotChangeEvent;
 240: import org.jfree.chart.event.RendererChangeEvent;
 241: import org.jfree.chart.event.RendererChangeListener;
 242: import org.jfree.chart.renderer.RendererUtilities;
 243: import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
 244: import org.jfree.chart.renderer.xy.XYItemRenderer;
 245: import org.jfree.chart.renderer.xy.XYItemRendererState;
 246: import org.jfree.data.Range;
 247: import org.jfree.data.general.Dataset;
 248: import org.jfree.data.general.DatasetChangeEvent;
 249: import org.jfree.data.general.DatasetUtilities;
 250: import org.jfree.data.xy.XYDataset;
 251: import org.jfree.io.SerialUtilities;
 252: import org.jfree.ui.Layer;
 253: import org.jfree.ui.RectangleEdge;
 254: import org.jfree.ui.RectangleInsets;
 255: import org.jfree.util.ObjectList;
 256: import org.jfree.util.ObjectUtilities;
 257: import org.jfree.util.PaintUtilities;
 258: import org.jfree.util.PublicCloneable;
 259: 
 260: /**
 261:  * A general class for plotting data in the form of (x, y) pairs.  This plot can
 262:  * use data from any class that implements the {@link XYDataset} interface.
 263:  * <P>
 264:  * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
 265:  * on the plot.  By using different renderers, various chart types can be
 266:  * produced.
 267:  * <p>
 268:  * The {@link org.jfree.chart.ChartFactory} class contains static methods for
 269:  * creating pre-configured charts.
 270:  */
 271: public class XYPlot extends Plot implements ValueAxisPlot, Zoomable,
 272:         RendererChangeListener, Cloneable, PublicCloneable, Serializable {
 273: 
 274:     /** For serialization. */
 275:     private static final long serialVersionUID = 7044148245716569264L;
 276:     
 277:     /** The default grid line stroke. */
 278:     public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
 279:             BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, 
 280:             new float[] {2.0f, 2.0f}, 0.0f);
 281: 
 282:     /** The default grid line paint. */
 283:     public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
 284: 
 285:     /** The default crosshair visibility. */
 286:     public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
 287: 
 288:     /** The default crosshair stroke. */
 289:     public static final Stroke DEFAULT_CROSSHAIR_STROKE
 290:             = DEFAULT_GRIDLINE_STROKE;
 291: 
 292:     /** The default crosshair paint. */
 293:     public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
 294: 
 295:     /** The resourceBundle for the localization. */
 296:     protected static ResourceBundle localizationResources 
 297:             = ResourceBundle.getBundle(
 298:                     "org.jfree.chart.plot.LocalizationBundle");
 299: 
 300:     /** The plot orientation. */
 301:     private PlotOrientation orientation;
 302: 
 303:     /** The offset between the data area and the axes. */
 304:     private RectangleInsets axisOffset;
 305: 
 306:     /** The domain axis / axes (used for the x-values). */
 307:     private ObjectList domainAxes;
 308: 
 309:     /** The domain axis locations. */
 310:     private ObjectList domainAxisLocations;
 311: 
 312:     /** The range axis (used for the y-values). */
 313:     private ObjectList rangeAxes;
 314: 
 315:     /** The range axis location. */
 316:     private ObjectList rangeAxisLocations;
 317: 
 318:     /** Storage for the datasets. */
 319:     private ObjectList datasets;
 320: 
 321:     /** Storage for the renderers. */
 322:     private ObjectList renderers;
 323: 
 324:     /**
 325:      * Storage for keys that map datasets/renderers to domain axes.  If the
 326:      * map contains no entry for a dataset, it is assumed to map to the
 327:      * primary domain axis (index = 0).
 328:      */
 329:     private Map datasetToDomainAxisMap;
 330: 
 331:     /**
 332:      * Storage for keys that map datasets/renderers to range axes. If the
 333:      * map contains no entry for a dataset, it is assumed to map to the
 334:      * primary domain axis (index = 0).
 335:      */
 336:     private Map datasetToRangeAxisMap;
 337: 
 338:     /** The origin point for the quadrants (if drawn). */
 339:     private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0);
 340: 
 341:     /** The paint used for each quadrant. */
 342:     private transient Paint[] quadrantPaint
 343:             = new Paint[] {null, null, null, null};
 344: 
 345:     /** A flag that controls whether the domain grid-lines are visible. */
 346:     private boolean domainGridlinesVisible;
 347: 
 348:     /** The stroke used to draw the domain grid-lines. */
 349:     private transient Stroke domainGridlineStroke;
 350: 
 351:     /** The paint used to draw the domain grid-lines. */
 352:     private transient Paint domainGridlinePaint;
 353: 
 354:     /** A flag that controls whether the range grid-lines are visible. */
 355:     private boolean rangeGridlinesVisible;
 356: 
 357:     /** The stroke used to draw the range grid-lines. */
 358:     private transient Stroke rangeGridlineStroke;
 359: 
 360:     /** The paint used to draw the range grid-lines. */
 361:     private transient Paint rangeGridlinePaint;
 362: 
 363:     /** 
 364:      * A flag that controls whether or not the zero baseline against the domain
 365:      * axis is visible.
 366:      * 
 367:      * @since 1.0.5
 368:      */
 369:     private boolean domainZeroBaselineVisible;
 370: 
 371:     /** 
 372:      * The stroke used for the zero baseline against the domain axis. 
 373:      * 
 374:      * @since 1.0.5
 375:      */
 376:     private transient Stroke domainZeroBaselineStroke;
 377: 
 378:     /** 
 379:      * The paint used for the zero baseline against the domain axis. 
 380:      * 
 381:      * @since 1.0.5
 382:      */
 383:     private transient Paint domainZeroBaselinePaint;
 384: 
 385:     /** 
 386:      * A flag that controls whether or not the zero baseline against the range
 387:      * axis is visible.
 388:      */
 389:     private boolean rangeZeroBaselineVisible;
 390: 
 391:     /** The stroke used for the zero baseline against the range axis. */
 392:     private transient Stroke rangeZeroBaselineStroke;
 393: 
 394:     /** The paint used for the zero baseline against the range axis. */
 395:     private transient Paint rangeZeroBaselinePaint;
 396: 
 397:     /** A flag that controls whether or not a domain crosshair is drawn..*/
 398:     private boolean domainCrosshairVisible;
 399: 
 400:     /** The domain crosshair value. */
 401:     private double domainCrosshairValue;
 402: 
 403:     /** The pen/brush used to draw the crosshair (if any). */
 404:     private transient Stroke domainCrosshairStroke;
 405: 
 406:     /** The color used to draw the crosshair (if any). */
 407:     private transient Paint domainCrosshairPaint;
 408: 
 409:     /**
 410:      * A flag that controls whether or not the crosshair locks onto actual
 411:      * data points.
 412:      */
 413:     private boolean domainCrosshairLockedOnData = true;
 414: 
 415:     /** A flag that controls whether or not a range crosshair is drawn..*/
 416:     private boolean rangeCrosshairVisible;
 417: 
 418:     /** The range crosshair value. */
 419:     private double rangeCrosshairValue;
 420: 
 421:     /** The pen/brush used to draw the crosshair (if any). */
 422:     private transient Stroke rangeCrosshairStroke;
 423: 
 424:     /** The color used to draw the crosshair (if any). */
 425:     private transient Paint rangeCrosshairPaint;
 426: 
 427:     /**
 428:      * A flag that controls whether or not the crosshair locks onto actual
 429:      * data points.
 430:      */
 431:     private boolean rangeCrosshairLockedOnData = true;
 432: 
 433:     /** A map of lists of foreground markers (optional) for the domain axes. */
 434:     private Map foregroundDomainMarkers;
 435: 
 436:     /** A map of lists of background markers (optional) for the domain axes. */
 437:     private Map backgroundDomainMarkers;
 438: 
 439:     /** A map of lists of foreground markers (optional) for the range axes. */
 440:     private Map foregroundRangeMarkers;
 441: 
 442:     /** A map of lists of background markers (optional) for the range axes. */
 443:     private Map backgroundRangeMarkers;
 444: 
 445:     /** 
 446:      * A (possibly empty) list of annotations for the plot.  The list should
 447:      * be initialised in the constructor and never allowed to be 
 448:      * <code>null</code>.
 449:      */
 450:     private List annotations;
 451: 
 452:     /** The paint used for the domain tick bands (if any). */
 453:     private transient Paint domainTickBandPaint;
 454: 
 455:     /** The paint used for the range tick bands (if any). */
 456:     private transient Paint rangeTickBandPaint;
 457: 
 458:     /** The fixed domain axis space. */
 459:     private AxisSpace fixedDomainAxisSpace;
 460: 
 461:     /** The fixed range axis space. */
 462:     private AxisSpace fixedRangeAxisSpace;
 463: 
 464:     /**
 465:      * The order of the dataset rendering (REVERSE draws the primary dataset
 466:      * last so that it appears to be on top).
 467:      */
 468:     private DatasetRenderingOrder datasetRenderingOrder
 469:             = DatasetRenderingOrder.REVERSE;
 470: 
 471:     /**
 472:      * The order of the series rendering (REVERSE draws the primary series
 473:      * last so that it appears to be on top).
 474:      */
 475:     private SeriesRenderingOrder seriesRenderingOrder
 476:             = SeriesRenderingOrder.REVERSE;
 477: 
 478:     /**
 479:      * The weight for this plot (only relevant if this is a subplot in a
 480:      * combined plot).
 481:      */
 482:     private int weight;
 483: 
 484:     /**
 485:      * An optional collection of legend items that can be returned by the
 486:      * getLegendItems() method.
 487:      */
 488:     private LegendItemCollection fixedLegendItems;
 489: 
 490:     /**
 491:      * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
 492:      * no renderer.  You should specify these items before using the plot.
 493:      */
 494:     public XYPlot() {
 495:         this(null, null, null, null);
 496:     }
 497: 
 498:     /**
 499:      * Creates a new plot with the specified dataset, axes and renderer.  Any
 500:      * of the arguments can be <code>null</code>, but in that case you should
 501:      * take care to specify the value before using the plot (otherwise a
 502:      * <code>NullPointerException</code> may be thrown).
 503:      *
 504:      * @param dataset  the dataset (<code>null</code> permitted).
 505:      * @param domainAxis  the domain axis (<code>null</code> permitted).
 506:      * @param rangeAxis  the range axis (<code>null</code> permitted).
 507:      * @param renderer  the renderer (<code>null</code> permitted).
 508:      */
 509:     public XYPlot(XYDataset dataset,
 510:                   ValueAxis domainAxis,
 511:                   ValueAxis rangeAxis,
 512:                   XYItemRenderer renderer) {
 513: 
 514:         super();
 515: 
 516:         this.orientation = PlotOrientation.VERTICAL;
 517:         this.weight = 1;  // only relevant when this is a subplot
 518:         this.axisOffset = RectangleInsets.ZERO_INSETS;
 519: 
 520:         // allocate storage for datasets, axes and renderers (all optional)
 521:         this.domainAxes = new ObjectList();
 522:         this.domainAxisLocations = new ObjectList();
 523:         this.foregroundDomainMarkers = new HashMap();
 524:         this.backgroundDomainMarkers = new HashMap();
 525: 
 526:         this.rangeAxes = new ObjectList();
 527:         this.rangeAxisLocations = new ObjectList();
 528:         this.foregroundRangeMarkers = new HashMap();
 529:         this.backgroundRangeMarkers = new HashMap();
 530: 
 531:         this.datasets = new ObjectList();
 532:         this.renderers = new ObjectList();
 533: 
 534:         this.datasetToDomainAxisMap = new TreeMap();
 535:         this.datasetToRangeAxisMap = new TreeMap();
 536: 
 537:         this.datasets.set(0, dataset);
 538:         if (dataset != null) {
 539:             dataset.addChangeListener(this);
 540:         }
 541: 
 542:         this.renderers.set(0, renderer);
 543:         if (renderer != null) {
 544:             renderer.setPlot(this);
 545:             renderer.addChangeListener(this);
 546:         }
 547: 
 548:         this.domainAxes.set(0, domainAxis);
 549:         this.mapDatasetToDomainAxis(0, 0);
 550:         if (domainAxis != null) {
 551:             domainAxis.setPlot(this);
 552:             domainAxis.addChangeListener(this);
 553:         }
 554:         this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
 555: 
 556:         this.rangeAxes.set(0, rangeAxis);
 557:         this.mapDatasetToRangeAxis(0, 0);
 558:         if (rangeAxis != null) {
 559:             rangeAxis.setPlot(this);
 560:             rangeAxis.addChangeListener(this);
 561:         }
 562:         this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
 563: 
 564:         configureDomainAxes();
 565:         configureRangeAxes();
 566: 
 567:         this.domainGridlinesVisible = true;
 568:         this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
 569:         this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
 570: 
 571:         this.domainZeroBaselineVisible = false;
 572:         this.domainZeroBaselinePaint = Color.black;
 573:         this.domainZeroBaselineStroke = new BasicStroke(0.5f);
 574: 
 575:         this.rangeGridlinesVisible = true;
 576:         this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
 577:         this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
 578: 
 579:         this.rangeZeroBaselineVisible = false;
 580:         this.rangeZeroBaselinePaint = Color.black;
 581:         this.rangeZeroBaselineStroke = new BasicStroke(0.5f);
 582: 
 583:         this.domainCrosshairVisible = false;
 584:         this.domainCrosshairValue = 0.0;
 585:         this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
 586:         this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
 587: 
 588:         this.rangeCrosshairVisible = false;
 589:         this.rangeCrosshairValue = 0.0;
 590:         this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
 591:         this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
 592: 
 593:         this.annotations = new java.util.ArrayList();
 594: 
 595:     }
 596: 
 597:     /**
 598:      * Returns the plot type as a string.
 599:      *
 600:      * @return A short string describing the type of plot.
 601:      */
 602:     public String getPlotType() {
 603:         return localizationResources.getString("XY_Plot");
 604:     }
 605: 
 606:     /**
 607:      * Returns the orientation of the plot.
 608:      *
 609:      * @return The orientation (never <code>null</code>).
 610:      * 
 611:      * @see #setOrientation(PlotOrientation)
 612:      */
 613:     public PlotOrientation getOrientation() {
 614:         return this.orientation;
 615:     }
 616: 
 617:     /**
 618:      * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
 619:      * all registered listeners.
 620:      *
 621:      * @param orientation  the orientation (<code>null</code> not allowed).
 622:      * 
 623:      * @see #getOrientation()
 624:      */
 625:     public void setOrientation(PlotOrientation orientation) {
 626:         if (orientation == null) {
 627:             throw new IllegalArgumentException("Null 'orientation' argument.");
 628:         }
 629:         if (orientation != this.orientation) {
 630:             this.orientation = orientation;
 631:             notifyListeners(new PlotChangeEvent(this));
 632:         }
 633:     }
 634: 
 635:     /**
 636:      * Returns the axis offset.
 637:      *
 638:      * @return The axis offset (never <code>null</code>).
 639:      * 
 640:      * @see #setAxisOffset(RectangleInsets)
 641:      */
 642:     public RectangleInsets getAxisOffset() {
 643:         return this.axisOffset;
 644:     }
 645: 
 646:     /**
 647:      * Sets the axis offsets (gap between the data area and the axes) and sends
 648:      * a {@link PlotChangeEvent} to all registered listeners.
 649:      *
 650:      * @param offset  the offset (<code>null</code> not permitted).
 651:      * 
 652:      * @see #getAxisOffset()
 653:      */
 654:     public void setAxisOffset(RectangleInsets offset) {
 655:         if (offset == null) {
 656:             throw new IllegalArgumentException("Null 'offset' argument.");
 657:         }
 658:         this.axisOffset = offset;
 659:         notifyListeners(new PlotChangeEvent(this));
 660:     }
 661: 
 662:     /**
 663:      * Returns the domain axis with index 0.  If the domain axis for this plot
 664:      * is <code>null</code>, then the method will return the parent plot's 
 665:      * domain axis (if there is a parent plot).
 666:      *
 667:      * @return The domain axis (possibly <code>null</code>).
 668:      * 
 669:      * @see #getDomainAxis(int)
 670:      * @see #setDomainAxis(ValueAxis)
 671:      */
 672:     public ValueAxis getDomainAxis() {
 673:         return getDomainAxis(0);
 674:     }
 675: 
 676:     /**
 677:      * Returns the domain axis with the specified index, or <code>null</code>.
 678:      *
 679:      * @param index  the axis index.
 680:      *
 681:      * @return The axis (<code>null</code> possible).
 682:      * 
 683:      * @see #setDomainAxis(int, ValueAxis)
 684:      */
 685:     public ValueAxis getDomainAxis(int index) {
 686:         ValueAxis result = null;
 687:         if (index < this.domainAxes.size()) {
 688:             result = (ValueAxis) this.domainAxes.get(index);
 689:         }
 690:         if (result == null) {
 691:             Plot parent = getParent();
 692:             if (parent instanceof XYPlot) {
 693:                 XYPlot xy = (XYPlot) parent;
 694:                 result = xy.getDomainAxis(index);
 695:             }
 696:         }
 697:         return result;
 698:     }
 699: 
 700:     /**
 701:      * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
 702:      * to all registered listeners.
 703:      *
 704:      * @param axis  the new axis (<code>null</code> permitted).
 705:      * 
 706:      * @see #getDomainAxis()
 707:      * @see #setDomainAxis(int, ValueAxis)
 708:      */
 709:     public void setDomainAxis(ValueAxis axis) {
 710:         setDomainAxis(0, axis);
 711:     }
 712: 
 713:     /**
 714:      * Sets a domain axis and sends a {@link PlotChangeEvent} to all
 715:      * registered listeners.
 716:      *
 717:      * @param index  the axis index.
 718:      * @param axis  the axis (<code>null</code> permitted).
 719:      * 
 720:      * @see #getDomainAxis(int)
 721:      * @see #setRangeAxis(int, ValueAxis)
 722:      */
 723:     public void setDomainAxis(int index, ValueAxis axis) {
 724:         setDomainAxis(index, axis, true);
 725:     }
 726:     
 727:     /**
 728:      * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
 729:      * all registered listeners.
 730:      *
 731:      * @param index  the axis index.
 732:      * @param axis  the axis.
 733:      * @param notify  notify listeners?
 734:      * 
 735:      * @see #getDomainAxis(int)
 736:      */
 737:     public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
 738:         ValueAxis existing = getDomainAxis(index);
 739:         if (existing != null) {
 740:             existing.removeChangeListener(this);
 741:         }
 742:         if (axis != null) {
 743:             axis.setPlot(this);
 744:         }
 745:         this.domainAxes.set(index, axis);
 746:         if (axis != null) {
 747:             axis.configure();
 748:             axis.addChangeListener(this);
 749:         }
 750:         if (notify) {
 751:             notifyListeners(new PlotChangeEvent(this));
 752:         }
 753:     }
 754: 
 755:     /**
 756:      * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
 757:      * to all registered listeners.
 758:      * 
 759:      * @param axes  the axes (<code>null</code> not permitted).
 760:      * 
 761:      * @see #setRangeAxes(ValueAxis[])
 762:      */
 763:     public void setDomainAxes(ValueAxis[] axes) {
 764:         for (int i = 0; i < axes.length; i++) {
 765:             setDomainAxis(i, axes[i], false);   
 766:         }
 767:         notifyListeners(new PlotChangeEvent(this));
 768:     }
 769:     
 770:     /**
 771:      * Returns the location of the primary domain axis.
 772:      *
 773:      * @return The location (never <code>null</code>).
 774:      * 
 775:      * @see #setDomainAxisLocation(AxisLocation)
 776:      */
 777:     public AxisLocation getDomainAxisLocation() {
 778:         return (AxisLocation) this.domainAxisLocations.get(0);
 779:     }
 780: 
 781:     /**
 782:      * Sets the location of the primary domain axis and sends a 
 783:      * {@link PlotChangeEvent} to all registered listeners.
 784:      *
 785:      * @param location  the location (<code>null</code> not permitted).
 786:      * 
 787:      * @see #getDomainAxisLocation()
 788:      */
 789:     public void setDomainAxisLocation(AxisLocation location) {
 790:         // delegate...
 791:         setDomainAxisLocation(0, location, true);
 792:     }
 793: 
 794:     /**
 795:      * Sets the location of the domain axis and, if requested, sends a
 796:      * {@link PlotChangeEvent} to all registered listeners.
 797:      *
 798:      * @param location  the location (<code>null</code> not permitted).
 799:      * @param notify  notify listeners?
 800:      * 
 801:      * @see #getDomainAxisLocation()
 802:      */
 803:     public void setDomainAxisLocation(AxisLocation location, boolean notify) {
 804:         // delegate...
 805:         setDomainAxisLocation(0, location, notify);
 806:     }
 807: 
 808:     /**
 809:      * Returns the edge for the primary domain axis (taking into account the
 810:      * plot's orientation).
 811:      *
 812:      * @return The edge.
 813:      * 
 814:      * @see #getDomainAxisLocation()
 815:      * @see #getOrientation()
 816:      */
 817:     public RectangleEdge getDomainAxisEdge() {
 818:         return Plot.resolveDomainAxisLocation(getDomainAxisLocation(), 
 819:                 this.orientation);
 820:     }
 821: 
 822:     /**
 823:      * Returns the number of domain axes.
 824:      *
 825:      * @return The axis count.
 826:      * 
 827:      * @see #getRangeAxisCount()
 828:      */
 829:     public int getDomainAxisCount() {
 830:         return this.domainAxes.size();
 831:     }
 832: 
 833:     /**
 834:      * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
 835:      * to all registered listeners.
 836:      * 
 837:      * @see #clearRangeAxes()
 838:      */
 839:     public void clearDomainAxes() {
 840:         for (int i = 0; i < this.domainAxes.size(); i++) {
 841:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
 842:             if (axis != null) {
 843:                 axis.removeChangeListener(this);
 844:             }
 845:         }
 846:         this.domainAxes.clear();
 847:         notifyListeners(new PlotChangeEvent(this));
 848:     }
 849: 
 850:     /**
 851:      * Configures the domain axes. 
 852:      */
 853:     public void configureDomainAxes() {
 854:         for (int i = 0; i < this.domainAxes.size(); i++) {
 855:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
 856:             if (axis != null) {
 857:                 axis.configure();
 858:             }
 859:         }
 860:     }
 861: 
 862:     /**
 863:      * Returns the location for a domain axis.  If this hasn't been set
 864:      * explicitly, the method returns the location that is opposite to the
 865:      * primary domain axis location.
 866:      *
 867:      * @param index  the axis index.
 868:      *
 869:      * @return The location (never <code>null</code>).
 870:      * 
 871:      * @see #setDomainAxisLocation(int, AxisLocation)
 872:      */
 873:     public AxisLocation getDomainAxisLocation(int index) {
 874:         AxisLocation result = null;
 875:         if (index < this.domainAxisLocations.size()) {
 876:             result = (AxisLocation) this.domainAxisLocations.get(index);
 877:         }
 878:         if (result == null) {
 879:             result = AxisLocation.getOpposite(getDomainAxisLocation());
 880:         }
 881:         return result;
 882:     }
 883: 
 884:     /**
 885:      * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
 886:      * to all registered listeners.
 887:      *
 888:      * @param index  the axis index.
 889:      * @param location  the location (<code>null</code> not permitted for index
 890:      *     0).
 891:      * 
 892:      * @see #getDomainAxisLocation(int)
 893:      */
 894:     public void setDomainAxisLocation(int index, AxisLocation location) {
 895:         // delegate...
 896:         setDomainAxisLocation(index, location, true);
 897:     }
 898: 
 899:     /**
 900:      * Sets the axis location for a domain axis and, if requested, sends a
 901:      * {@link PlotChangeEvent} to all registered listeners.
 902:      * 
 903:      * @param index  the axis index.
 904:      * @param location  the location (<code>null</code> not permitted for 
 905:      *     index 0).
 906:      * @param notify  notify listeners?
 907:      * 
 908:      * @since 1.0.5
 909:      * 
 910:      * @see #getDomainAxisLocation(int)
 911:      * @see #setRangeAxisLocation(int, AxisLocation, boolean)
 912:      */
 913:     public void setDomainAxisLocation(int index, AxisLocation location, 
 914:             boolean notify) {
 915:         
 916:         if (index == 0 && location == null) {
 917:             throw new IllegalArgumentException(
 918:                     "Null 'location' for index 0 not permitted.");
 919:         }
 920:         this.domainAxisLocations.set(index, location);
 921:         if (notify) {
 922:             notifyListeners(new PlotChangeEvent(this));
 923:         }        
 924:     }
 925: 
 926:     /**
 927:      * Returns the edge for a domain axis.
 928:      *
 929:      * @param index  the axis index.
 930:      *
 931:      * @return The edge.
 932:      * 
 933:      * @see #getRangeAxisEdge(int)
 934:      */
 935:     public RectangleEdge getDomainAxisEdge(int index) {
 936:         AxisLocation location = getDomainAxisLocation(index);
 937:         RectangleEdge result = Plot.resolveDomainAxisLocation(location, 
 938:                 this.orientation);
 939:         if (result == null) {
 940:             result = RectangleEdge.opposite(getDomainAxisEdge());
 941:         }
 942:         return result;
 943:     }
 944: 
 945:     /**
 946:      * Returns the range axis for the plot.  If the range axis for this plot is
 947:      * <code>null</code>, then the method will return the parent plot's range 
 948:      * axis (if there is a parent plot).
 949:      *
 950:      * @return The range axis.
 951:      * 
 952:      * @see #getRangeAxis(int)
 953:      * @see #setRangeAxis(ValueAxis)
 954:      */
 955:     public ValueAxis getRangeAxis() {
 956:         return getRangeAxis(0);
 957:     }
 958: 
 959:     /**
 960:      * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
 961:      * all registered listeners.
 962:      *
 963:      * @param axis  the axis (<code>null</code> permitted).
 964:      *
 965:      * @see #getRangeAxis()
 966:      * @see #setRangeAxis(int, ValueAxis)
 967:      */
 968:     public void setRangeAxis(ValueAxis axis)  {
 969: 
 970:         if (axis != null) {
 971:             axis.setPlot(this);
 972:         }
 973: 
 974:         // plot is likely registered as a listener with the existing axis...
 975:         ValueAxis existing = getRangeAxis();
 976:         if (existing != null) {
 977:             existing.removeChangeListener(this);
 978:         }
 979: 
 980:         this.rangeAxes.set(0, axis);
 981:         if (axis != null) {
 982:             axis.configure();
 983:             axis.addChangeListener(this);
 984:         }
 985:         notifyListeners(new PlotChangeEvent(this));
 986: 
 987:     }
 988: 
 989:     /**
 990:      * Returns the location of the primary range axis.
 991:      *
 992:      * @return The location (never <code>null</code>).
 993:      * 
 994:      * @see #setRangeAxisLocation(AxisLocation)
 995:      */
 996:     public AxisLocation getRangeAxisLocation() {
 997:         return (AxisLocation) this.rangeAxisLocations.get(0);
 998:     }
 999: 
1000:     /**
1001:      * Sets the location of the primary range axis and sends a
1002:      * {@link PlotChangeEvent} to all registered listeners.
1003:      *
1004:      * @param location  the location (<code>null</code> not permitted).
1005:      * 
1006:      * @see #getRangeAxisLocation()
1007:      */
1008:     public void setRangeAxisLocation(AxisLocation location) {
1009:         // delegate...
1010:         setRangeAxisLocation(0, location, true);
1011:     }
1012: 
1013:     /**
1014:      * Sets the location of the primary range axis and, if requested, sends a
1015:      * {@link PlotChangeEvent} to all registered listeners.
1016:      *
1017:      * @param location  the location (<code>null</code> not permitted).
1018:      * @param notify  notify listeners?
1019:      * 
1020:      * @see #getRangeAxisLocation()
1021:      */
1022:     public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1023:         // delegate...
1024:         setRangeAxisLocation(0, location, notify);
1025:     }
1026: 
1027:     /**
1028:      * Returns the edge for the primary range axis.
1029:      *
1030:      * @return The range axis edge.
1031:      * 
1032:      * @see #getRangeAxisLocation()
1033:      * @see #getOrientation()
1034:      */
1035:     public RectangleEdge getRangeAxisEdge() {
1036:         return Plot.resolveRangeAxisLocation(getRangeAxisLocation(), 
1037:                 this.orientation);
1038:     }
1039: 
1040:     /**
1041:      * Returns a range axis.
1042:      *
1043:      * @param index  the axis index.
1044:      *
1045:      * @return The axis (<code>null</code> possible).
1046:      * 
1047:      * @see #setRangeAxis(int, ValueAxis)
1048:      */
1049:     public ValueAxis getRangeAxis(int index) {
1050:         ValueAxis result = null;
1051:         if (index < this.rangeAxes.size()) {
1052:             result = (ValueAxis) this.rangeAxes.get(index);
1053:         }
1054:         if (result == null) {
1055:             Plot parent = getParent();
1056:             if (parent instanceof XYPlot) {
1057:                 XYPlot xy = (XYPlot) parent;
1058:                 result = xy.getRangeAxis(index);
1059:             }
1060:         }
1061:         return result;
1062:     }
1063: 
1064:     /**
1065:      * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1066:      * listeners.
1067:      *
1068:      * @param index  the axis index.
1069:      * @param axis  the axis (<code>null</code> permitted).
1070:      * 
1071:      * @see #getRangeAxis(int)
1072:      */
1073:     public void setRangeAxis(int index, ValueAxis axis) {
1074:         setRangeAxis(index, axis, true);
1075:     } 
1076:     
1077:     /**
1078:      * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to 
1079:      * all registered listeners.
1080:      *
1081:      * @param index  the axis index.
1082:      * @param axis  the axis (<code>null</code> permitted).
1083:      * @param notify  notify listeners?
1084:      * 
1085:      * @see #getRangeAxis(int)
1086:      */
1087:     public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1088:         ValueAxis existing = getRangeAxis(index);
1089:         if (existing != null) {
1090:             existing.removeChangeListener(this);
1091:         }
1092:         if (axis != null) {
1093:             axis.setPlot(this);
1094:         }
1095:         this.rangeAxes.set(index, axis);
1096:         if (axis != null) {
1097:             axis.configure();
1098:             axis.addChangeListener(this);
1099:         }
1100:         if (notify) {
1101:             notifyListeners(new PlotChangeEvent(this));
1102:         }
1103:     }
1104: 
1105:     /**
1106:      * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1107:      * to all registered listeners.
1108:      * 
1109:      * @param axes  the axes (<code>null</code> not permitted).
1110:      * 
1111:      * @see #setDomainAxes(ValueAxis[])
1112:      */
1113:     public void setRangeAxes(ValueAxis[] axes) {
1114:         for (int i = 0; i < axes.length; i++) {
1115:             setRangeAxis(i, axes[i], false);   
1116:         }
1117:         notifyListeners(new PlotChangeEvent(this));
1118:     }
1119:     
1120:     /**
1121:      * Returns the number of range axes.
1122:      *
1123:      * @return The axis count.
1124:      * 
1125:      * @see #getDomainAxisCount()
1126:      */
1127:     public int getRangeAxisCount() {
1128:         return this.rangeAxes.size();
1129:     }
1130: 
1131:     /**
1132:      * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1133:      * to all registered listeners.
1134:      * 
1135:      * @see #clearDomainAxes()
1136:      */
1137:     public void clearRangeAxes() {
1138:         for (int i = 0; i < this.rangeAxes.size(); i++) {
1139:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1140:             if (axis != null) {
1141:                 axis.removeChangeListener(this);
1142:             }
1143:         }
1144:         this.rangeAxes.clear();
1145:         notifyListeners(new PlotChangeEvent(this));
1146:     }
1147: 
1148:     /**
1149:      * Configures the range axes.
1150:      * 
1151:      * @see #configureDomainAxes()
1152:      */
1153:     public void configureRangeAxes() {
1154:         for (int i = 0; i < this.rangeAxes.size(); i++) {
1155:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1156:             if (axis != null) {
1157:                 axis.configure();
1158:             }
1159:         }
1160:     }
1161: 
1162:     /**
1163:      * Returns the location for a range axis.  If this hasn't been set
1164:      * explicitly, the method returns the location that is opposite to the
1165:      * primary range axis location.
1166:      *
1167:      * @param index  the axis index.
1168:      *
1169:      * @return The location (never <code>null</code>).
1170:      * 
1171:      * @see #setRangeAxisLocation(int, AxisLocation)
1172:      */
1173:     public AxisLocation getRangeAxisLocation(int index) {
1174:         AxisLocation result = null;
1175:         if (index < this.rangeAxisLocations.size()) {
1176:             result = (AxisLocation) this.rangeAxisLocations.get(index);
1177:         }
1178:         if (result == null) {
1179:             result = AxisLocation.getOpposite(getRangeAxisLocation());
1180:         }
1181:         return result;
1182:     }
1183: 
1184:     /**
1185:      * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1186:      * to all registered listeners.
1187:      *
1188:      * @param index  the axis index.
1189:      * @param location  the location (<code>null</code> permitted).
1190:      * 
1191:      * @see #getRangeAxisLocation(int)
1192:      */
1193:     public void setRangeAxisLocation(int index, AxisLocation location) {
1194:         // delegate...
1195:         setRangeAxisLocation(index, location, true);
1196:     }
1197:     
1198:     /**
1199:      * Sets the axis location for a domain axis and, if requested, sends a
1200:      * {@link PlotChangeEvent} to all registered listeners.
1201:      * 
1202:      * @param index  the axis index.
1203:      * @param location  the location (<code>null</code> not permitted for 
1204:      *     index 0).
1205:      * @param notify  notify listeners?
1206:      * 
1207:      * @since 1.0.5
1208:      * 
1209:      * @see #getRangeAxisLocation(int)
1210:      * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1211:      */
1212:     public void setRangeAxisLocation(int index, AxisLocation location, 
1213:             boolean notify) {
1214:         
1215:         if (index == 0 && location == null) {
1216:             throw new IllegalArgumentException(
1217:                     "Null 'location' for index 0 not permitted.");
1218:         }
1219:         this.rangeAxisLocations.set(index, location);
1220:         if (notify) {
1221:             notifyListeners(new PlotChangeEvent(this));
1222:         }   
1223:     }
1224: 
1225:     /**
1226:      * Returns the edge for a range axis.
1227:      *
1228:      * @param index  the axis index.
1229:      *
1230:      * @return The edge.
1231:      * 
1232:      * @see #getRangeAxisLocation(int)
1233:      * @see #getOrientation()
1234:      */
1235:     public RectangleEdge getRangeAxisEdge(int index) {
1236:         AxisLocation location = getRangeAxisLocation(index);
1237:         RectangleEdge result = Plot.resolveRangeAxisLocation(location, 
1238:                 this.orientation);
1239:         if (result == null) {
1240:             result = RectangleEdge.opposite(getRangeAxisEdge());
1241:         }
1242:         return result;
1243:     }
1244: 
1245:     /**
1246:      * Returns the primary dataset for the plot.
1247:      *
1248:      * @return The primary dataset (possibly <code>null</code>).
1249:      * 
1250:      * @see #getDataset(int)
1251:      * @see #setDataset(XYDataset)
1252:      */
1253:     public XYDataset getDataset() {
1254:         return getDataset(0);
1255:     }
1256: 
1257:     /**
1258:      * Returns a dataset.
1259:      *
1260:      * @param index  the dataset index.
1261:      *
1262:      * @return The dataset (possibly <code>null</code>).
1263:      * 
1264:      * @see #setDataset(int, XYDataset)
1265:      */
1266:     public XYDataset getDataset(int index) {
1267:         XYDataset result = null;
1268:         if (this.datasets.size() > index) {
1269:             result = (XYDataset) this.datasets.get(index);
1270:         }
1271:         return result;
1272:     }
1273: 
1274:     /**
1275:      * Sets the primary dataset for the plot, replacing the existing dataset if
1276:      * there is one.
1277:      *
1278:      * @param dataset  the dataset (<code>null</code> permitted).
1279:      * 
1280:      * @see #getDataset()
1281:      * @see #setDataset(int, XYDataset)
1282:      */
1283:     public void setDataset(XYDataset dataset) {
1284:         setDataset(0, dataset);
1285:     }
1286: 
1287:     /**
1288:      * Sets a dataset for the plot.
1289:      *
1290:      * @param index  the dataset index.
1291:      * @param dataset  the dataset (<code>null</code> permitted).
1292:      * 
1293:      * @see #getDataset(int)
1294:      */
1295:     public void setDataset(int index, XYDataset dataset) {
1296:         XYDataset existing = getDataset(index);
1297:         if (existing != null) {
1298:             existing.removeChangeListener(this);
1299:         }
1300:         this.datasets.set(index, dataset);
1301:         if (dataset != null) {
1302:             dataset.addChangeListener(this);
1303:         }
1304: 
1305:         // send a dataset change event to self...
1306:         DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1307:         datasetChanged(event);
1308:     }
1309: 
1310:     /**
1311:      * Returns the number of datasets.
1312:      *
1313:      * @return The number of datasets.
1314:      */
1315:     public int getDatasetCount() {
1316:         return this.datasets.size();
1317:     }
1318: 
1319:     /**
1320:      * Returns the index of the specified dataset, or <code>-1</code> if the
1321:      * dataset does not belong to the plot.
1322:      *
1323:      * @param dataset  the dataset (<code>null</code> not permitted).
1324:      *
1325:      * @return The index.
1326:      */
1327:     public int indexOf(XYDataset dataset) {
1328:         int result = -1;
1329:         for (int i = 0; i < this.datasets.size(); i++) {
1330:             if (dataset == this.datasets.get(i)) {
1331:                 result = i;
1332:                 break;
1333:             }
1334:         }
1335:         return result;
1336:     }
1337: 
1338:     /**
1339:      * Maps a dataset to a particular domain axis.  All data will be plotted
1340:      * against axis zero by default, no mapping is required for this case.
1341:      *
1342:      * @param index  the dataset index (zero-based).
1343:      * @param axisIndex  the axis index.
1344:      * 
1345:      * @see #mapDatasetToRangeAxis(int, int)
1346:      */
1347:     public void mapDatasetToDomainAxis(int index, int axisIndex) {
1348:         this.datasetToDomainAxisMap.put(new Integer(index), 
1349:                 new Integer(axisIndex));
1350:         // fake a dataset change event to update axes...
1351:         datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1352:     }
1353: 
1354:     /**
1355:      * Maps a dataset to a particular range axis.  All data will be plotted
1356:      * against axis zero by default, no mapping is required for this case.
1357:      *
1358:      * @param index  the dataset index (zero-based).
1359:      * @param axisIndex  the axis index.
1360:      * 
1361:      * @see #mapDatasetToDomainAxis(int, int)
1362:      */
1363:     public void mapDatasetToRangeAxis(int index, int axisIndex) {
1364:         this.datasetToRangeAxisMap.put(new Integer(index), 
1365:                 new Integer(axisIndex));
1366:         // fake a dataset change event to update axes...
1367:         datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1368:     }
1369: 
1370:     /**
1371:      * Returns the renderer for the primary dataset.
1372:      *
1373:      * @return The item renderer (possibly <code>null</code>).
1374:      * 
1375:      * @see #setRenderer(XYItemRenderer)
1376:      */
1377:     public XYItemRenderer getRenderer() {
1378:         return getRenderer(0);
1379:     }
1380: 
1381:     /**
1382:      * Returns the renderer for a dataset, or <code>null</code>.
1383:      *
1384:      * @param index  the renderer index.
1385:      *
1386:      * @return The renderer (possibly <code>null</code>).
1387:      * 
1388:      * @see #setRenderer(int, XYItemRenderer)
1389:      */
1390:     public XYItemRenderer getRenderer(int index) {
1391:         XYItemRenderer result = null;
1392:         if (this.renderers.size() > index) {
1393:             result = (XYItemRenderer) this.renderers.get(index);
1394:         }
1395:         return result;
1396: 
1397:     }
1398: 
1399:     /**
1400:      * Sets the renderer for the primary dataset and sends a
1401:      * {@link PlotChangeEvent} to all registered listeners.  If the renderer
1402:      * is set to <code>null</code>, no data will be displayed.
1403:      *
1404:      * @param renderer  the renderer (<code>null</code> permitted).
1405:      * 
1406:      * @see #getRenderer()
1407:      */
1408:     public void setRenderer(XYItemRenderer renderer) {
1409:         setRenderer(0, renderer);
1410:     }
1411: 
1412:     /**
1413:      * Sets a renderer and sends a {@link PlotChangeEvent} to all
1414:      * registered listeners.
1415:      *
1416:      * @param index  the index.
1417:      * @param renderer  the renderer.
1418:      * 
1419:      * @see #getRenderer(int)
1420:      */
1421:     public void setRenderer(int index, XYItemRenderer renderer) {
1422:         setRenderer(index, renderer, true);
1423:     }
1424: 
1425:     /**
1426:      * Sets a renderer and sends a {@link PlotChangeEvent} to all
1427:      * registered listeners.
1428:      *
1429:      * @param index  the index.
1430:      * @param renderer  the renderer.
1431:      * @param notify  notify listeners?
1432:      * 
1433:      * @see #getRenderer(int)
1434:      */
1435:     public void setRenderer(int index, XYItemRenderer renderer, 
1436:                             boolean notify) {
1437:         XYItemRenderer existing = getRenderer(index);
1438:         if (existing != null) {
1439:             existing.removeChangeListener(this);
1440:         }
1441:         this.renderers.set(index, renderer);
1442:         if (renderer != null) {
1443:             renderer.setPlot(this);
1444:             renderer.addChangeListener(this);
1445:         }
1446:         configureDomainAxes();
1447:         configureRangeAxes();
1448:         if (notify) {
1449:             notifyListeners(new PlotChangeEvent(this));
1450:         }
1451:     }
1452: 
1453:     /**
1454:      * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1455:      * to all registered listeners.
1456:      * 
1457:      * @param renderers  the renderers (<code>null</code> not permitted).
1458:      */
1459:     public void setRenderers(XYItemRenderer[] renderers) {
1460:         for (int i = 0; i < renderers.length; i++) {
1461:             setRenderer(i, renderers[i], false);   
1462:         }
1463:         notifyListeners(new PlotChangeEvent(this));
1464:     }
1465:     
1466:     /**
1467:      * Returns the dataset rendering order.
1468:      *
1469:      * @return The order (never <code>null</code>).
1470:      * 
1471:      * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1472:      */
1473:     public DatasetRenderingOrder getDatasetRenderingOrder() {
1474:         return this.datasetRenderingOrder;
1475:     }
1476: 
1477:     /**
1478:      * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1479:      * registered listeners.  By default, the plot renders the primary dataset
1480:      * last (so that the primary dataset overlays the secondary datasets).
1481:      * You can reverse this if you want to.
1482:      *
1483:      * @param order  the rendering order (<code>null</code> not permitted).
1484:      * 
1485:      * @see #getDatasetRenderingOrder()
1486:      */
1487:     public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1488:         if (order == null) {
1489:             throw new IllegalArgumentException("Null 'order' argument.");
1490:         }
1491:         this.datasetRenderingOrder = order;
1492:         notifyListeners(new PlotChangeEvent(this));
1493:     }
1494: 
1495:     /**
1496:      * Returns the series rendering order.
1497:      *
1498:      * @return the order (never <code>null</code>).
1499:      * 
1500:      * @see #setSeriesRenderingOrder(SeriesRenderingOrder)
1501:      */
1502:     public SeriesRenderingOrder getSeriesRenderingOrder() {
1503:         return this.seriesRenderingOrder;
1504:     }
1505: 
1506:     /**
1507:      * Sets the series order and sends a {@link PlotChangeEvent} to all
1508:      * registered listeners.  By default, the plot renders the primary series
1509:      * last (so that the primary series appears to be on top).
1510:      * You can reverse this if you want to.
1511:      *
1512:      * @param order  the rendering order (<code>null</code> not permitted).
1513:      * 
1514:      * @see #getSeriesRenderingOrder()
1515:      */
1516:     public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1517:         if (order == null) {
1518:             throw new IllegalArgumentException("Null 'order' argument.");
1519:         }
1520:         this.seriesRenderingOrder = order;
1521:         notifyListeners(new PlotChangeEvent(this));
1522:     }
1523: 
1524:     /**
1525:      * Returns the index of the specified renderer, or <code>-1</code> if the
1526:      * renderer is not assigned to this plot.
1527:      *
1528:      * @param renderer  the renderer (<code>null</code> permitted).
1529:      *
1530:      * @return The renderer index.
1531:      */
1532:     public int getIndexOf(XYItemRenderer renderer) {
1533:         return this.renderers.indexOf(renderer);
1534:     }
1535: 
1536:     /**
1537:      * Returns the renderer for the specified dataset.  The code first
1538:      * determines the index of the dataset, then checks if there is a
1539:      * renderer with the same index (if not, the method returns renderer(0).
1540:      *
1541:      * @param dataset  the dataset (<code>null</code> permitted).
1542:      *
1543:      * @return The renderer (possibly <code>null</code>).
1544:      */
1545:     public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1546:         XYItemRenderer result = null;
1547:         for (int i = 0; i < this.datasets.size(); i++) {
1548:             if (this.datasets.get(i) == dataset) {
1549:                 result = (XYItemRenderer) this.renderers.get(i);
1550:                 if (result == null) {
1551:                     result = getRenderer();
1552:                 }
1553:                 break;
1554:             }
1555:         }
1556:         return result;
1557:     }
1558: 
1559:     /**
1560:      * Returns the weight for this plot when it is used as a subplot within a
1561:      * combined plot.
1562:      *
1563:      * @return The weight.
1564:      * 
1565:      * @see #setWeight(int)
1566:      */
1567:     public int getWeight() {
1568:         return this.weight;
1569:     }
1570: 
1571:     /**
1572:      * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all
1573:      * registered listeners.
1574:      *
1575:      * @param weight  the weight.
1576:      * 
1577:      * @see #getWeight()
1578:      */
1579:     public void setWeight(int weight) {
1580:         this.weight = weight;
1581:         notifyListeners(new PlotChangeEvent(this));
1582:     }
1583: 
1584:     /**
1585:      * Returns <code>true</code> if the domain gridlines are visible, and
1586:      * <code>false<code> otherwise.
1587:      *
1588:      * @return <code>true</code> or <code>false</code>.
1589:      * 
1590:      * @see #setDomainGridlinesVisible(boolean)
1591:      */
1592:     public boolean isDomainGridlinesVisible() {
1593:         return this.domainGridlinesVisible;
1594:     }
1595: 
1596:     /**
1597:      * Sets the flag that controls whether or not the domain grid-lines are
1598:      * visible.
1599:      * <p>
1600:      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1601:      * registered listeners.
1602:      *
1603:      * @param visible  the new value of the flag.
1604:      * 
1605:      * @see #isDomainGridlinesVisible()
1606:      */
1607:     public void setDomainGridlinesVisible(boolean visible) {
1608:         if (this.domainGridlinesVisible != visible) {
1609:             this.domainGridlinesVisible = visible;
1610:             notifyListeners(new PlotChangeEvent(this));
1611:         }
1612:     }
1613: 
1614:     /**
1615:      * Returns the stroke for the grid-lines (if any) plotted against the
1616:      * domain axis.
1617:      *
1618:      * @return The stroke (never <code>null</code>).
1619:      * 
1620:      * @see #setDomainGridlineStroke(Stroke)
1621:      */
1622:     public Stroke getDomainGridlineStroke() {
1623:         return this.domainGridlineStroke;
1624:     }
1625: 
1626:     /**
1627:      * Sets the stroke for the grid lines plotted against the domain axis, and
1628:      * sends a {@link PlotChangeEvent} to all registered listeners.
1629:      * <p>
1630:      * If you set this to <code>null</code>, no grid lines will be drawn.
1631:      *
1632:      * @param stroke  the stroke (<code>null</code> not permitted).
1633:      * 
1634:      * @throws IllegalArgumentException if <code>stroke</code> is 
1635:      *     <code>null</code>.
1636:      *
1637:      * @see #getDomainGridlineStroke()
1638:      */
1639:     public void setDomainGridlineStroke(Stroke stroke) {
1640:         if (stroke == null) {
1641:             throw new IllegalArgumentException("Null 'stroke' argument.");
1642:         }
1643:         this.domainGridlineStroke = stroke;
1644:         notifyListeners(new PlotChangeEvent(this));
1645:     }
1646: 
1647:     /**
1648:      * Returns the paint for the grid lines (if any) plotted against the domain
1649:      * axis.
1650:      *
1651:      * @return The paint (never <code>null</code>).
1652:      * 
1653:      * @see #setDomainGridlinePaint(Paint)
1654:      */
1655:     public Paint getDomainGridlinePaint() {
1656:         return this.domainGridlinePaint;
1657:     }
1658: 
1659:     /**
1660:      * Sets the paint for the grid lines plotted against the domain axis, and
1661:      * sends a {@link PlotChangeEvent} to all registered listeners.
1662:      *
1663:      * @param paint  the paint (<code>null</code> not permitted).
1664:      * 
1665:      * @throws IllegalArgumentException if <code>paint</code> is 
1666:      *     <code>null</code>.
1667:      * 
1668:      * @see #getDomainGridlinePaint()
1669:      */
1670:     public void setDomainGridlinePaint(Paint paint) {
1671:         if (paint == null) {
1672:             throw new IllegalArgumentException("Null 'paint' argument.");
1673:         }
1674:         this.domainGridlinePaint = paint;
1675:         notifyListeners(new PlotChangeEvent(this));
1676:     }
1677: 
1678:     /**
1679:      * Returns <code>true</code> if the range axis grid is visible, and
1680:      * <code>false<code> otherwise.
1681:      *
1682:      * @return A boolean.
1683:      * 
1684:      * @see #setRangeGridlinesVisible(boolean)
1685:      */
1686:     public boolean isRangeGridlinesVisible() {
1687:         return this.rangeGridlinesVisible;
1688:     }
1689: 
1690:     /**
1691:      * Sets the flag that controls whether or not the range axis grid lines
1692:      * are visible.
1693:      * <p>
1694:      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1695:      * registered listeners.
1696:      *
1697:      * @param visible  the new value of the flag.
1698:      * 
1699:      * @see #isRangeGridlinesVisible()
1700:      */
1701:     public void setRangeGridlinesVisible(boolean visible) {
1702:         if (this.rangeGridlinesVisible != visible) {
1703:             this.rangeGridlinesVisible = visible;
1704:             notifyListeners(new PlotChangeEvent(this));
1705:         }
1706:     }
1707: 
1708:     /**
1709:      * Returns the stroke for the grid lines (if any) plotted against the
1710:      * range axis.
1711:      *
1712:      * @return The stroke (never <code>null</code>).
1713:      * 
1714:      * @see #setRangeGridlineStroke(Stroke)
1715:      */
1716:     public Stroke getRangeGridlineStroke() {
1717:         return this.rangeGridlineStroke;
1718:     }
1719: 
1720:     /**
1721:      * Sets the stroke for the grid lines plotted against the range axis,
1722:      * and sends a {@link PlotChangeEvent} to all registered listeners.
1723:      *
1724:      * @param stroke  the stroke (<code>null</code> not permitted).
1725:      * 
1726:      * @see #getRangeGridlineStroke()
1727:      */
1728:     public void setRangeGridlineStroke(Stroke stroke) {
1729:         if (stroke == null) {
1730:             throw new IllegalArgumentException("Null 'stroke' argument.");
1731:         }
1732:         this.rangeGridlineStroke = stroke;
1733:         notifyListeners(new PlotChangeEvent(this));
1734:     }
1735: 
1736:     /**
1737:      * Returns the paint for the grid lines (if any) plotted against the range
1738:      * axis.
1739:      *
1740:      * @return The paint (never <code>null</code>).
1741:      * 
1742:      * @see #setRangeGridlinePaint(Paint)
1743:      */
1744:     public Paint getRangeGridlinePaint() {
1745:         return this.rangeGridlinePaint;
1746:     }
1747: 
1748:     /**
1749:      * Sets the paint for the grid lines plotted against the range axis and
1750:      * sends a {@link PlotChangeEvent} to all registered listeners.
1751:      *
1752:      * @param paint  the paint (<code>null</code> not permitted).
1753:      * 
1754:      * @see #getRangeGridlinePaint()
1755:      */
1756:     public void setRangeGridlinePaint(Paint paint) {
1757:         if (paint == null) {
1758:             throw new IllegalArgumentException("Null 'paint' argument.");
1759:         }
1760:         this.rangeGridlinePaint = paint;
1761:         notifyListeners(new PlotChangeEvent(this));
1762:     }
1763: 
1764:     /**
1765:      * Returns a flag that controls whether or not a zero baseline is
1766:      * displayed for the domain axis.
1767:      *
1768:      * @return A boolean.
1769:      * 
1770:      * @since 1.0.5
1771:      * 
1772:      * @see #setDomainZeroBaselineVisible(boolean)
1773:      */
1774:     public boolean isDomainZeroBaselineVisible() {
1775:         return this.domainZeroBaselineVisible;
1776:     }
1777: 
1778:     /**
1779:      * Sets the flag that controls whether or not the zero baseline is
1780:      * displayed for the domain axis, and sends a {@link PlotChangeEvent} to
1781:      * all registered listeners.
1782:      *
1783:      * @param visible  the flag.
1784:      * 
1785:      * @since 1.0.5
1786:      * 
1787:      * @see #isDomainZeroBaselineVisible()
1788:      */
1789:     public void setDomainZeroBaselineVisible(boolean visible) {
1790:         this.domainZeroBaselineVisible = visible;
1791:         notifyListeners(new PlotChangeEvent(this));
1792:     }
1793: 
1794:     /**
1795:      * Returns the stroke used for the zero baseline against the domain axis.
1796:      *
1797:      * @return The stroke (never <code>null</code>).
1798:      * 
1799:      * @since 1.0.5
1800:      * 
1801:      * @see #setDomainZeroBaselineStroke(Stroke)
1802:      */
1803:     public Stroke getDomainZeroBaselineStroke() {
1804:         return this.domainZeroBaselineStroke;
1805:     }
1806: 
1807:     /**
1808:      * Sets the stroke for the zero baseline for the domain axis,
1809:      * and sends a {@link PlotChangeEvent} to all registered listeners.
1810:      *
1811:      * @param stroke  the stroke (<code>null</code> not permitted).
1812:      * 
1813:      * @since 1.0.5
1814:      * 
1815:      * @see #getRangeZeroBaselineStroke()
1816:      */
1817:     public void setDomainZeroBaselineStroke(Stroke stroke) {
1818:         if (stroke == null) {
1819:             throw new IllegalArgumentException("Null 'stroke' argument.");
1820:         }
1821:         this.domainZeroBaselineStroke = stroke;
1822:         notifyListeners(new PlotChangeEvent(this));
1823:     }
1824: 
1825:     /**
1826:      * Returns the paint for the zero baseline (if any) plotted against the
1827:      * domain axis.
1828:      * 
1829:      * @since 1.0.5
1830:      *
1831:      * @return The paint (never <code>null</code>).
1832:      * 
1833:      * @see #setDomainZeroBaselinePaint(Paint)
1834:      */
1835:     public Paint getDomainZeroBaselinePaint() {
1836:         return this.domainZeroBaselinePaint;
1837:     }
1838: 
1839:     /**
1840:      * Sets the paint for the zero baseline plotted against the domain axis and
1841:      * sends a {@link PlotChangeEvent} to all registered listeners.
1842:      *
1843:      * @param paint  the paint (<code>null</code> not permitted).
1844:      * 
1845:      * @since 1.0.5
1846:      * 
1847:      * @see #getDomainZeroBaselinePaint()
1848:      */
1849:     public void setDomainZeroBaselinePaint(Paint paint) {
1850:         if (paint == null) {
1851:             throw new IllegalArgumentException("Null 'paint' argument.");
1852:         }
1853:         this.domainZeroBaselinePaint = paint;
1854:         notifyListeners(new PlotChangeEvent(this));
1855:     }
1856:     
1857:     /**
1858:      * Returns a flag that controls whether or not a zero baseline is
1859:      * displayed for the range axis.
1860:      *
1861:      * @return A boolean.
1862:      * 
1863:      * @see #setRangeZeroBaselineVisible(boolean)
1864:      */
1865:     public boolean isRangeZeroBaselineVisible() {
1866:         return this.rangeZeroBaselineVisible;
1867:     }
1868: 
1869:     /**
1870:      * Sets the flag that controls whether or not the zero baseline is
1871:      * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1872:      * all registered listeners.
1873:      *
1874:      * @param visible  the flag.
1875:      * 
1876:      * @see #isRangeZeroBaselineVisible()
1877:      */
1878:     public void setRangeZeroBaselineVisible(boolean visible) {
1879:         this.rangeZeroBaselineVisible = visible;
1880:         notifyListeners(new PlotChangeEvent(this));
1881:     }
1882: 
1883:     /**
1884:      * Returns the stroke used for the zero baseline against the range axis.
1885:      *
1886:      * @return The stroke (never <code>null</code>).
1887:      * 
1888:      * @see #setRangeZeroBaselineStroke(Stroke)
1889:      */
1890:     public Stroke getRangeZeroBaselineStroke() {
1891:         return this.rangeZeroBaselineStroke;
1892:     }
1893: 
1894:     /**
1895:      * Sets the stroke for the zero baseline for the range axis,
1896:      * and sends a {@link PlotChangeEvent} to all registered listeners.
1897:      *
1898:      * @param stroke  the stroke (<code>null</code> not permitted).
1899:      * 
1900:      * @see #getRangeZeroBaselineStroke()
1901:      */
1902:     public void setRangeZeroBaselineStroke(Stroke stroke) {
1903:         if (stroke == null) {
1904:             throw new IllegalArgumentException("Null 'stroke' argument.");
1905:         }
1906:         this.rangeZeroBaselineStroke = stroke;
1907:         notifyListeners(new PlotChangeEvent(this));
1908:     }
1909: 
1910:     /**
1911:      * Returns the paint for the zero baseline (if any) plotted against the
1912:      * range axis.
1913:      *
1914:      * @return The paint (never <code>null</code>).
1915:      * 
1916:      * @see #setRangeZeroBaselinePaint(Paint)
1917:      */
1918:     public Paint getRangeZeroBaselinePaint() {
1919:         return this.rangeZeroBaselinePaint;
1920:     }
1921: 
1922:     /**
1923:      * Sets the paint for the zero baseline plotted against the range axis and
1924:      * sends a {@link PlotChangeEvent} to all registered listeners.
1925:      *
1926:      * @param paint  the paint (<code>null</code> not permitted).
1927:      * 
1928:      * @see #getRangeZeroBaselinePaint()
1929:      */
1930:     public void setRangeZeroBaselinePaint(Paint paint) {
1931:         if (paint == null) {
1932:             throw new IllegalArgumentException("Null 'paint' argument.");
1933:         }
1934:         this.rangeZeroBaselinePaint = paint;
1935:         notifyListeners(new PlotChangeEvent(this));
1936:     }
1937: 
1938:     /**
1939:      * Returns the paint used for the domain tick bands.  If this is
1940:      * <code>null</code>, no tick bands will be drawn.
1941:      *
1942:      * @return The paint (possibly <code>null</code>).
1943:      * 
1944:      * @see #setDomainTickBandPaint(Paint)
1945:      */
1946:     public Paint getDomainTickBandPaint() {
1947:         return this.domainTickBandPaint;
1948:     }
1949: 
1950:     /**
1951:      * Sets the paint for the domain tick bands.
1952:      *
1953:      * @param paint  the paint (<code>null</code> permitted).
1954:      * 
1955:      * @see #getDomainTickBandPaint()
1956:      */
1957:     public void setDomainTickBandPaint(Paint paint) {
1958:         this.domainTickBandPaint = paint;
1959:         notifyListeners(new PlotChangeEvent(this));
1960:     }
1961: 
1962:     /**
1963:      * Returns the paint used for the range tick bands.  If this is
1964:      * <code>null</code>, no tick bands will be drawn.
1965:      *
1966:      * @return The paint (possibly <code>null</code>).
1967:      * 
1968:      * @see #setRangeTickBandPaint(Paint)
1969:      */
1970:     public Paint getRangeTickBandPaint() {
1971:         return this.rangeTickBandPaint;
1972:     }
1973: 
1974:     /**
1975:      * Sets the paint for the range tick bands.
1976:      *
1977:      * @param paint  the paint (<code>null</code> permitted).
1978:      * 
1979:      * @see #getRangeTickBandPaint()
1980:      */
1981:     public void setRangeTickBandPaint(Paint paint) {
1982:         this.rangeTickBandPaint = paint;
1983:         notifyListeners(new PlotChangeEvent(this));
1984:     }
1985: 
1986:     /**
1987:      * Returns the origin for the quadrants that can be displayed on the plot.
1988:      * This defaults to (0, 0).
1989:      *
1990:      * @return The origin point (never <code>null</code>).
1991:      * 
1992:      * @see #setQuadrantOrigin(Point2D)
1993:      */
1994:     public Point2D getQuadrantOrigin() {
1995:         return this.quadrantOrigin;
1996:     }
1997: 
1998:     /**
1999:      * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
2000:      * registered listeners.
2001:      *
2002:      * @param origin  the origin (<code>null</code> not permitted).
2003:      * 
2004:      * @see #getQuadrantOrigin()
2005:      */
2006:     public void setQuadrantOrigin(Point2D origin) {
2007:         if (origin == null) {
2008:             throw new IllegalArgumentException("Null 'origin' argument.");
2009:         }
2010:         this.quadrantOrigin = origin;
2011:         notifyListeners(new PlotChangeEvent(this));
2012:     }
2013: 
2014:     /**
2015:      * Returns the paint used for the specified quadrant.
2016:      *
2017:      * @param index  the quadrant index (0-3).
2018:      *
2019:      * @return The paint (possibly <code>null</code>).
2020:      * 
2021:      * @see #setQuadrantPaint(int, Paint)
2022:      */
2023:     public Paint getQuadrantPaint(int index) {
2024:         if (index < 0 || index > 3) {
2025:             throw new IllegalArgumentException("The index value (" + index 
2026:                     + ") should be in the range 0 to 3.");
2027:         }
2028:         return this.quadrantPaint[index];
2029:     }
2030: 
2031:     /**
2032:      * Sets the paint used for the specified quadrant and sends a
2033:      * {@link PlotChangeEvent} to all registered listeners.
2034:      *
2035:      * @param index  the quadrant index (0-3).
2036:      * @param paint  the paint (<code>null</code> permitted).
2037:      * 
2038:      * @see #getQuadrantPaint(int)
2039:      */
2040:     public void setQuadrantPaint(int index, Paint paint) {
2041:         if (index < 0 || index > 3) {
2042:             throw new IllegalArgumentException("The index value (" + index 
2043:                     + ") should be in the range 0 to 3.");
2044:         }
2045:         this.quadrantPaint[index] = paint;
2046:         notifyListeners(new PlotChangeEvent(this));
2047:     }
2048: 
2049:     /**
2050:      * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
2051:      * to all registered listeners.
2052:      * <P>
2053:      * Typically a marker will be drawn by the renderer as a line perpendicular
2054:      * to the range axis, however this is entirely up to the renderer.
2055:      *
2056:      * @param marker  the marker (<code>null</code> not permitted).
2057:      * 
2058:      * @see #addDomainMarker(Marker, Layer)
2059:      * @see #clearDomainMarkers()
2060:      */
2061:     public void addDomainMarker(Marker marker) {
2062:         // defer argument checking...
2063:         addDomainMarker(marker, Layer.FOREGROUND);
2064:     }
2065: 
2066:     /**
2067:      * Adds a marker for the domain axis in the specified layer and sends a
2068:      * {@link PlotChangeEvent} to all registered listeners.
2069:      * <P>
2070:      * Typically a marker will be drawn by the renderer as a line perpendicular
2071:      * to the range axis, however this is entirely up to the renderer.
2072:      *
2073:      * @param marker  the marker (<code>null</code> not permitted).
2074:      * @param layer  the layer (foreground or background).
2075:      * 
2076:      * @see #addDomainMarker(int, Marker, Layer)
2077:      */
2078:     public void addDomainMarker(Marker marker, Layer layer) {
2079:         addDomainMarker(0, marker, layer);
2080:     }
2081: 
2082:     /**
2083:      * Clears all the (foreground and background) domain markers and sends a
2084:      * {@link PlotChangeEvent} to all registered listeners.
2085:      * 
2086:      * @see #addDomainMarker(int, Marker, Layer)
2087:      */
2088:     public void clearDomainMarkers() {
2089:         if (this.backgroundDomainMarkers != null) {
2090:             Set keys = this.backgroundDomainMarkers.keySet();
2091:             Iterator iterator = keys.iterator();
2092:             while (iterator.hasNext()) {
2093:                 Integer key = (Integer) iterator.next();
2094:                 clearDomainMarkers(key.intValue());
2095:             }
2096:             this.backgroundDomainMarkers.clear();
2097:         }
2098:         if (this.foregroundDomainMarkers != null) {
2099:             Set keys = this.foregroundDomainMarkers.keySet();
2100:             Iterator iterator = keys.iterator();
2101:             while (iterator.hasNext()) {
2102:                 Integer key = (Integer) iterator.next();
2103:                 clearDomainMarkers(key.intValue());
2104:             }
2105:             this.foregroundDomainMarkers.clear();
2106:         }
2107:         notifyListeners(new PlotChangeEvent(this));
2108:     }
2109: 
2110:     /**
2111:      * Clears the (foreground and background) domain markers for a particular
2112:      * renderer.
2113:      *
2114:      * @param index  the renderer index.
2115:      * 
2116:      * @see #clearRangeMarkers(int)
2117:      */
2118:     public void clearDomainMarkers(int index) {
2119:         Integer key = new Integer(index);
2120:         if (this.backgroundDomainMarkers != null) {
2121:             Collection markers
2122:                 = (Collection) this.backgroundDomainMarkers.get(key);
2123:             if (markers != null) {
2124:                 Iterator iterator = markers.iterator();
2125:                 while (iterator.hasNext()) {
2126:                     Marker m = (Marker) iterator.next();
2127:                     m.removeChangeListener(this);
2128:                 }
2129:                 markers.clear();
2130:             }
2131:         }
2132:         if (this.foregroundRangeMarkers != null) {
2133:             Collection markers
2134:                 = (Collection) this.foregroundDomainMarkers.get(key);
2135:             if (markers != null) {
2136:                 Iterator iterator = markers.iterator();
2137:                 while (iterator.hasNext()) {
2138:                     Marker m = (Marker) iterator.next();
2139:                     m.removeChangeListener(this);
2140:                 }
2141:                 markers.clear();
2142:             }
2143:         }
2144:         notifyListeners(new PlotChangeEvent(this));
2145:     }
2146: 
2147:     /**
2148:      * Adds a marker for a specific dataset/renderer and sends a 
2149:      * {@link PlotChangeEvent} to all registered listeners.
2150:      * <P>
2151:      * Typically a marker will be drawn by the renderer as a line perpendicular
2152:      * to the domain axis (that the renderer is mapped to), however this is
2153:      * entirely up to the renderer.
2154:      *
2155:      * @param index  the dataset/renderer index.
2156:      * @param marker  the marker.
2157:      * @param layer  the layer (foreground or background).
2158:      * 
2159:      * @see #clearDomainMarkers(int)
2160:      * @see #addRangeMarker(int, Marker, Layer)
2161:      */
2162:     public void addDomainMarker(int index, Marker marker, Layer layer) {
2163:         if (marker == null) {
2164:             throw new IllegalArgumentException("Null 'marker' not permitted.");
2165:         }
2166:         if (layer == null) {
2167:             throw new IllegalArgumentException("Null 'layer' not permitted.");
2168:         }
2169:         Collection markers;
2170:         if (layer == Layer.FOREGROUND) {
2171:             markers = (Collection) this.foregroundDomainMarkers.get(
2172:                     new Integer(index));
2173:             if (markers == null) {
2174:                 markers = new java.util.ArrayList();
2175:                 this.foregroundDomainMarkers.put(new Integer(index), markers);
2176:             }
2177:             markers.add(marker);
2178:         }
2179:         else if (layer == Layer.BACKGROUND) {
2180:             markers = (Collection) this.backgroundDomainMarkers.get(
2181:                     new Integer(index));
2182:             if (markers == null) {
2183:                 markers = new java.util.ArrayList();
2184:                 this.backgroundDomainMarkers.put(new Integer(index), markers);
2185:             }
2186:             markers.add(marker);
2187:         }
2188:         marker.addChangeListener(this);
2189:         notifyListeners(new PlotChangeEvent(this));
2190:     }
2191: 
2192:     /**
2193:      * Removes a marker for the domain axis and sends a {@link PlotChangeEvent} 
2194:      * to all registered listeners.
2195:      *
2196:      * @param marker  the marker.
2197:      *
2198:      * @return A boolean indicating whether or not the marker was actually 
2199:      *         removed.
2200:      *
2201:      * @since 1.0.7
2202:      */
2203:     public boolean removeDomainMarker(Marker marker) {
2204:         return removeDomainMarker(marker, Layer.FOREGROUND);
2205:     }
2206: 
2207:     /**
2208:      * Removes a marker for the domain axis in the specified layer and sends a
2209:      * {@link PlotChangeEvent} to all registered listeners.
2210:      *
2211:      * @param marker the marker (<code>null</code> not permitted).
2212:      * @param layer the layer (foreground or background).
2213:      *
2214:      * @return A boolean indicating whether or not the marker was actually 
2215:      *         removed.
2216:      *
2217:      * @since 1.0.7
2218:      */
2219:     public boolean removeDomainMarker(Marker marker, Layer layer) {
2220:         return removeDomainMarker(0, marker, layer);
2221:     }
2222: 
2223:     /**
2224:      * Removes a marker for a specific dataset/renderer and sends a
2225:      * {@link PlotChangeEvent} to all registered listeners.
2226:      *
2227:      * @param index the dataset/renderer index.
2228:      * @param marker the marker.
2229:      * @param layer the layer (foreground or background).
2230:      *
2231:      * @return A boolean indicating whether or not the marker was actually 
2232:      *         removed.
2233:      *
2234:      * @since 1.0.7
2235:      */
2236:     public boolean removeDomainMarker(int index, Marker marker, Layer layer) {
2237:         ArrayList markers;
2238:         if (layer == Layer.FOREGROUND) {
2239:             markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer(
2240:                     index));
2241:         }
2242:         else {
2243:             markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer(
2244:                     index));
2245:         }
2246:         boolean removed = markers.remove(marker);
2247:         if (removed) {
2248:             notifyListeners(new PlotChangeEvent(this));
2249:         }
2250:         return removed;
2251:     }
2252:     
2253:     /**
2254:      * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
2255:      * all registered listeners.
2256:      * <P>
2257:      * Typically a marker will be drawn by the renderer as a line perpendicular
2258:      * to the range axis, however this is entirely up to the renderer.
2259:      *
2260:      * @param marker  the marker (<code>null</code> not permitted).
2261:      * 
2262:      * @see #addRangeMarker(Marker, Layer)
2263:      */
2264:     public void addRangeMarker(Marker marker) {
2265:         addRangeMarker(marker, Layer.FOREGROUND);
2266:     }
2267: 
2268:     /**
2269:      * Adds a marker for the range axis in the specified layer and sends a
2270:      * {@link PlotChangeEvent} to all registered listeners.
2271:      * <P>
2272:      * Typically a marker will be drawn by the renderer as a line perpendicular
2273:      * to the range axis, however this is entirely up to the renderer.
2274:      *
2275:      * @param marker  the marker (<code>null</code> not permitted).
2276:      * @param layer  the layer (foreground or background).
2277:      * 
2278:      * @see #addRangeMarker(int, Marker, Layer)
2279:      */
2280:     public void addRangeMarker(Marker marker, Layer layer) {
2281:         addRangeMarker(0, marker, layer);
2282:     }
2283: 
2284:     /**
2285:      * Clears all the range markers and sends a {@link PlotChangeEvent} to all
2286:      * registered listeners.
2287:      * 
2288:      * @see #clearRangeMarkers()
2289:      */
2290:     public void clearRangeMarkers() {
2291:         if (this.backgroundRangeMarkers != null) {
2292:             Set keys = this.backgroundRangeMarkers.keySet();
2293:             Iterator iterator = keys.iterator();
2294:             while (iterator.hasNext()) {
2295:                 Integer key = (Integer) iterator.next();
2296:                 clearRangeMarkers(key.intValue());
2297:             }
2298:             this.backgroundRangeMarkers.clear();
2299:         }
2300:         if (this.foregroundRangeMarkers != null) {
2301:             Set keys = this.foregroundRangeMarkers.keySet();
2302:             Iterator iterator = keys.iterator();
2303:             while (iterator.hasNext()) {
2304:                 Integer key = (Integer) iterator.next();
2305:                 clearRangeMarkers(key.intValue());
2306:             }
2307:             this.foregroundRangeMarkers.clear();
2308:         }
2309:         notifyListeners(new PlotChangeEvent(this));
2310:     }
2311: 
2312:     /**
2313:      * Adds a marker for a specific dataset/renderer and sends a 
2314:      * {@link PlotChangeEvent} to all registered listeners.
2315:      * <P>
2316:      * Typically a marker will be drawn by the renderer as a line perpendicular
2317:      * to the range axis, however this is entirely up to the renderer.
2318:      *
2319:      * @param index  the dataset/renderer index.
2320:      * @param marker  the marker.
2321:      * @param layer  the layer (foreground or background).
2322:      * 
2323:      * @see #clearRangeMarkers(int)
2324:      * @see #addDomainMarker(int, Marker, Layer)
2325:      */
2326:     public void addRangeMarker(int index, Marker marker, Layer layer) {
2327:         Collection markers;
2328:         if (layer == Layer.FOREGROUND) {
2329:             markers = (Collection) this.foregroundRangeMarkers.get(
2330:                     new Integer(index));
2331:             if (markers == null) {
2332:                 markers = new java.util.ArrayList();
2333:                 this.foregroundRangeMarkers.put(new Integer(index), markers);
2334:             }
2335:             markers.add(marker);
2336:         }
2337:         else if (layer == Layer.BACKGROUND) {
2338:             markers = (Collection) this.backgroundRangeMarkers.get(
2339:                     new Integer(index));
2340:             if (markers == null) {
2341:                 markers = new java.util.ArrayList();
2342:                 this.backgroundRangeMarkers.put(new Integer(index), markers);
2343:             }
2344:             markers.add(marker);
2345:         }
2346:         marker.addChangeListener(this);
2347:         notifyListeners(new PlotChangeEvent(this));
2348:     }
2349: 
2350:     /**
2351:      * Clears the (foreground and background) range markers for a particular
2352:      * renderer.
2353:      *
2354:      * @param index  the renderer index.
2355:      */
2356:     public void clearRangeMarkers(int index) {
2357:         Integer key = new Integer(index);
2358:         if (this.backgroundRangeMarkers != null) {
2359:             Collection markers
2360:                 = (Collection) this.backgroundRangeMarkers.get(key);
2361:             if (markers != null) {
2362:                 Iterator iterator = markers.iterator();
2363:                 while (iterator.hasNext()) {
2364:                     Marker m = (Marker) iterator.next();
2365:                     m.removeChangeListener(this);
2366:                 }
2367:                 markers.clear();
2368:             }
2369:         }
2370:         if (this.foregroundRangeMarkers != null) {
2371:             Collection markers
2372:                 = (Collection) this.foregroundRangeMarkers.get(key);
2373:             if (markers != null) {
2374:                 Iterator iterator = markers.iterator();
2375:                 while (iterator.hasNext()) {
2376:                     Marker m = (Marker) iterator.next();
2377:                     m.removeChangeListener(this);
2378:                 }
2379:                 markers.clear();
2380:             }
2381:         }
2382:         notifyListeners(new PlotChangeEvent(this));
2383:     }
2384: 
2385:     /**
2386:      * Removes a marker for the range axis and sends a {@link PlotChangeEvent} 
2387:      * to all registered listeners.
2388:      *
2389:      * @param marker the marker.
2390:      *
2391:      * @return A boolean indicating whether or not the marker was actually 
2392:      *         removed.
2393:      *
2394:      * @since 1.0.7
2395:      */
2396:     public boolean removeRangeMarker(Marker marker) {
2397:         return removeRangeMarker(marker, Layer.FOREGROUND);
2398:     }
2399: 
2400:     /**
2401:      * Removes a marker for the range axis in the specified layer and sends a
2402:      * {@link PlotChangeEvent} to all registered listeners.
2403:      *
2404:      * @param marker the marker (<code>null</code> not permitted).
2405:      * @param layer the layer (foreground or background).
2406:      *
2407:      * @return A boolean indicating whether or not the marker was actually 
2408:      *         removed.
2409:      *
2410:      * @since 1.0.7
2411:      */
2412:     public boolean removeRangeMarker(Marker marker, Layer layer) {
2413:         return removeRangeMarker(0, marker, layer);
2414:     }
2415: 
2416:     /**
2417:      * Removes a marker for a specific dataset/renderer and sends a
2418:      * {@link PlotChangeEvent} to all registered listeners.
2419:      *
2420:      * @param index the dataset/renderer index.
2421:      * @param marker the marker.
2422:      * @param layer the layer (foreground or background).
2423:      *
2424:      * @return A boolean indicating whether or not the marker was actually 
2425:      *         removed.
2426:      *
2427:      * @since 1.0.7
2428:      */
2429:     public boolean removeRangeMarker(int index, Marker marker, Layer layer) {
2430:         if (marker == null) {
2431:             throw new IllegalArgumentException("Null 'marker' argument.");
2432:         }
2433:         ArrayList markers;
2434:         if (layer == Layer.FOREGROUND) {
2435:             markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer(
2436:                     index));
2437:         }
2438:         else {
2439:             markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer(
2440:                     index));
2441:         }
2442: 
2443:         boolean removed = markers.remove(marker);
2444:         if (removed) {
2445:             notifyListeners(new PlotChangeEvent(this));
2446:         }
2447:         return removed;
2448:     }
2449: 
2450:     /**
2451:      * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to 
2452:      * all registered listeners.
2453:      *
2454:      * @param annotation  the annotation (<code>null</code> not permitted).
2455:      * 
2456:      * @see #getAnnotations()
2457:      * @see #removeAnnotation(XYAnnotation)
2458:      */
2459:     public void addAnnotation(XYAnnotation annotation) {
2460:         if (annotation == null) {
2461:             throw new IllegalArgumentException("Null 'annotation' argument.");
2462:         }
2463:         this.annotations.add(annotation);
2464:         notifyListeners(new PlotChangeEvent(this));
2465:     }
2466: 
2467:     /**
2468:      * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2469:      * to all registered listeners.
2470:      *
2471:      * @param annotation  the annotation (<code>null</code> not permitted).
2472:      *
2473:      * @return A boolean (indicates whether or not the annotation was removed).
2474:      * 
2475:      * @see #addAnnotation(XYAnnotation)
2476:      * @see #getAnnotations()
2477:      */
2478:     public boolean removeAnnotation(XYAnnotation annotation) {
2479:         if (annotation == null) {
2480:             throw new IllegalArgumentException("Null 'annotation' argument.");
2481:         }
2482:         boolean removed = this.annotations.remove(annotation);
2483:         if (removed) {
2484:             notifyListeners(new PlotChangeEvent(this));
2485:         }
2486:         return removed;
2487:     }
2488: 
2489:     /**
2490:      * Returns the list of annotations.
2491:      *
2492:      * @return The list of annotations.
2493:      * 
2494:      * @since 1.0.1
2495:      * 
2496:      * @see #addAnnotation(XYAnnotation)
2497:      */
2498:     public List getAnnotations() {
2499:         return new ArrayList(this.annotations);
2500:     }
2501: 
2502:     /**
2503:      * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2504:      * registered listeners.
2505:      * 
2506:      * @see #addAnnotation(XYAnnotation)
2507:      */
2508:     public void clearAnnotations() {
2509:         this.annotations.clear();
2510:         notifyListeners(new PlotChangeEvent(this));
2511:     }
2512:     
2513:     /**
2514:      * Calculates the space required for all the axes in the plot.
2515:      *
2516:      * @param g2  the graphics device.
2517:      * @param plotArea  the plot area.
2518:      *
2519:      * @return The required space.
2520:      */
2521:     protected AxisSpace calculateAxisSpace(Graphics2D g2,
2522:                                            Rectangle2D plotArea) {
2523:         AxisSpace space = new AxisSpace();
2524:         space = calculateDomainAxisSpace(g2, plotArea, space);
2525:         space = calculateRangeAxisSpace(g2, plotArea, space);
2526:         return space;
2527:     }
2528: 
2529:     /**
2530:      * Calculates the space required for the domain axis/axes.
2531:      *
2532:      * @param g2  the graphics device.
2533:      * @param plotArea  the plot area.
2534:      * @param space  a carrier for the result (<code>null</code> permitted).
2535:      *
2536:      * @return The required space.
2537:      */
2538:     protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
2539:                                                  Rectangle2D plotArea,
2540:                                                  AxisSpace space) {
2541: 
2542:         if (space == null) {
2543:             space = new AxisSpace();
2544:         }
2545: 
2546:         // reserve some space for the domain axis...
2547:         if (this.fixedDomainAxisSpace != null) {
2548:             if (this.orientation == PlotOrientation.HORIZONTAL) {
2549:                 space.ensureAtLeast(this.fixedDomainAxisSpace.getLeft(), 
2550:                         RectangleEdge.LEFT);
2551:                 space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(), 
2552:                         RectangleEdge.RIGHT);
2553:             }
2554:             else if (this.orientation == PlotOrientation.VERTICAL) {
2555:                 space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(), 
2556:                         RectangleEdge.TOP);
2557:                 space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(), 
2558:                         RectangleEdge.BOTTOM);
2559:             }
2560:         }
2561:         else {
2562:             // reserve space for the domain axes...
2563:             for (int i = 0; i < this.domainAxes.size(); i++) {
2564:                 Axis axis = (Axis) this.domainAxes.get(i);
2565:                 if (axis != null) {
2566:                     RectangleEdge edge = getDomainAxisEdge(i);
2567:                     space = axis.reserveSpace(g2, this, plotArea, edge, space);
2568:                 }
2569:             }
2570:         }
2571: 
2572:         return space;
2573: 
2574:     }
2575: 
2576:     /**
2577:      * Calculates the space required for the range axis/axes.
2578:      *
2579:      * @param g2  the graphics device.
2580:      * @param plotArea  the plot area.
2581:      * @param space  a carrier for the result (<code>null</code> permitted).
2582:      *
2583:      * @return The required space.
2584:      */
2585:     protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
2586:                                                 Rectangle2D plotArea,
2587:                                                 AxisSpace space) {
2588: 
2589:         if (space == null) {
2590:             space = new AxisSpace();
2591:         }
2592: 
2593:         // reserve some space for the range axis...
2594:         if (this.fixedRangeAxisSpace != null) {
2595:             if (this.orientation == PlotOrientation.HORIZONTAL) {
2596:                 space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(), 
2597:                         RectangleEdge.TOP);
2598:                 space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(), 
2599:                         RectangleEdge.BOTTOM);
2600:             }
2601:             else if (this.orientation == PlotOrientation.VERTICAL) {
2602:                 space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(), 
2603:                         RectangleEdge.LEFT);
2604:                 space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(), 
2605:                         RectangleEdge.RIGHT);
2606:             }
2607:         }
2608:         else {
2609:             // reserve space for the range axes...
2610:             for (int i = 0; i < this.rangeAxes.size(); i++) {
2611:                 Axis axis = (Axis) this.rangeAxes.get(i);
2612:                 if (axis != null) {
2613:                     RectangleEdge edge = getRangeAxisEdge(i);
2614:                     space = axis.reserveSpace(g2, this, plotArea, edge, space);
2615:                 }
2616:             }
2617:         }
2618:         return space;
2619: 
2620:     }
2621: 
2622:     /**
2623:      * Draws the plot within the specified area on a graphics device.
2624:      *
2625:      * @param g2  the graphics device.
2626:      * @param area  the plot area (in Java2D space).
2627:      * @param anchor  an anchor point in Java2D space (<code>null</code>
2628:      *                permitted).
2629:      * @param parentState  the state from the parent plot, if there is one
2630:      *                     (<code>null</code> permitted).
2631:      * @param info  collects chart drawing information (<code>null</code>
2632:      *              permitted).
2633:      */
2634:     public void draw(Graphics2D g2,
2635:                      Rectangle2D area,
2636:                      Point2D anchor,
2637:                      PlotState parentState,
2638:                      PlotRenderingInfo info) {
2639: 
2640:         // if the plot area is too small, just return...
2641:         boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2642:         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2643:         if (b1 || b2) {
2644:             return;
2645:         }
2646: 
2647:         // record the plot area...
2648:         if (info != null) {
2649:             info.setPlotArea(area);
2650:         }
2651: 
2652:         // adjust the drawing area for the plot insets (if any)...
2653:         RectangleInsets insets = getInsets();
2654:         insets.trim(area);
2655: 
2656:         AxisSpace space = calculateAxisSpace(g2, area);
2657:         Rectangle2D dataArea = space.shrink(area, null);
2658:         this.axisOffset.trim(dataArea);
2659: 
2660:         if (info != null) {
2661:             info.setDataArea(dataArea);
2662:         }
2663: 
2664:         // draw the plot background and axes...
2665:         drawBackground(g2, dataArea);
2666:         Map axisStateMap = drawAxes(g2, area, dataArea, info);
2667: 
2668:         PlotOrientation orient = getOrientation();
2669: 
2670:         // the anchor point is typically the point where the mouse last
2671:         // clicked - the crosshairs will be driven off this point...
2672:         if (anchor != null && !dataArea.contains(anchor)) {
2673:             anchor = null;
2674:         }
2675:         CrosshairState crosshairState = new CrosshairState();
2676:         crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
2677:         crosshairState.setAnchor(anchor);
2678:         
2679:         crosshairState.setAnchorX(Double.NaN);
2680:         crosshairState.setAnchorY(Double.NaN);            
2681:         if (anchor != null) {
2682:             ValueAxis domainAxis = getDomainAxis();
2683:             if (domainAxis != null) {
2684:                 double x;
2685:                 if (orient == PlotOrientation.VERTICAL) {
2686:                     x = domainAxis.java2DToValue(anchor.getX(), dataArea, 
2687:                             getDomainAxisEdge());
2688:                 } 
2689:                 else {
2690:                     x = domainAxis.java2DToValue(anchor.getY(), dataArea, 
2691:                             getDomainAxisEdge());
2692:                 }
2693:                 crosshairState.setAnchorX(x);
2694:             }
2695:             ValueAxis rangeAxis = getRangeAxis();
2696:             if (rangeAxis != null) {
2697:                 double y;
2698:                 if (orient == PlotOrientation.VERTICAL) {
2699:                     y = rangeAxis.java2DToValue(anchor.getY(), dataArea, 
2700:                             getRangeAxisEdge());
2701:                 } 
2702:                 else {
2703:                     y = rangeAxis.java2DToValue(anchor.getX(), dataArea, 
2704:                             getRangeAxisEdge());
2705:                 }
2706:                 crosshairState.setAnchorY(y);                
2707:             }
2708:         }
2709:         crosshairState.setCrosshairX(getDomainCrosshairValue());
2710:         crosshairState.setCrosshairY(getRangeCrosshairValue());
2711:         Shape originalClip = g2.getClip();
2712:         Composite originalComposite = g2.getComposite();
2713: 
2714:         g2.clip(dataArea);
2715:         g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 
2716:                 getForegroundAlpha()));
2717: 
2718:         AxisState domainAxisState = (AxisState) axisStateMap.get(
2719:                 getDomainAxis());
2720:         if (domainAxisState == null) {
2721:             if (parentState != null) {
2722:                 domainAxisState = (AxisState) parentState.getSharedAxisStates()
2723:                         .get(getDomainAxis());
2724:             }
2725:         }
2726: 
2727:         AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis());
2728:         if (rangeAxisState == null) {
2729:             if (parentState != null) {
2730:                 rangeAxisState = (AxisState) parentState.getSharedAxisStates()
2731:                         .get(getRangeAxis());
2732:             }
2733:         }
2734:         if (domainAxisState != null) {
2735:             drawDomainTickBands(g2, dataArea, domainAxisState.getTicks());
2736:         }
2737:         if (rangeAxisState != null) {
2738:             drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks());
2739:         }
2740:         if (domainAxisState != null) {
2741:             drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
2742:             drawZeroDomainBaseline(g2, dataArea);
2743:         }
2744:         if (rangeAxisState != null) {
2745:             drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2746:             drawZeroRangeBaseline(g2, dataArea);
2747:         }
2748: 
2749:         // draw the markers that are associated with a specific renderer...
2750:         for (int i = 0; i < this.renderers.size(); i++) {
2751:             drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2752:         }
2753:         for (int i = 0; i < this.renderers.size(); i++) {
2754:             drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2755:         }
2756: 
2757:         // now draw annotations and render data items...
2758:         boolean foundData = false;
2759:         DatasetRenderingOrder order = getDatasetRenderingOrder();
2760:         if (order == DatasetRenderingOrder.FORWARD) {
2761: 
2762:             // draw background annotations
2763:             int rendererCount = this.renderers.size();
2764:             for (int i = 0; i < rendererCount; i++) {
2765:                 XYItemRenderer r = getRenderer(i);
2766:                 if (r != null) {
2767:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2768:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2769:                     r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis,
2770:                             Layer.BACKGROUND, info);
2771:                 }
2772:             }
2773: 
2774:             // render data items...
2775:             for (int i = 0; i < getDatasetCount(); i++) {
2776:                 foundData = render(g2, dataArea, i, info, crosshairState)
2777:                     || foundData;
2778:             }
2779: 
2780:             // draw foreground annotations
2781:             for (int i = 0; i < rendererCount; i++) {
2782:                 XYItemRenderer r = getRenderer(i);
2783:                 if (r != null) {
2784:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2785:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2786:                     r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis,
2787:                             Layer.FOREGROUND, info);
2788:                 }
2789:             }
2790: 
2791:         }
2792:         else if (order == DatasetRenderingOrder.REVERSE) {
2793: 
2794:             // draw background annotations
2795:             int rendererCount = this.renderers.size();
2796:             for (int i = rendererCount - 1; i >= 0; i--) {
2797:                 XYItemRenderer r = getRenderer(i);
2798:                 if (i >= getDatasetCount()) { // we need the dataset to make
2799:                     continue;                 // a link to the axes
2800:                 }
2801:                 if (r != null) {
2802:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2803:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2804:                     r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis,
2805:                             Layer.BACKGROUND, info);
2806:                 }
2807:             }
2808: 
2809:             for (int i = getDatasetCount() - 1; i >= 0; i--) {
2810:                 foundData = render(g2, dataArea, i, info, crosshairState)
2811:                     || foundData;
2812:             }
2813: 
2814:             // draw foreground annotations
2815:             for (int i = rendererCount - 1; i >= 0; i--) {
2816:                 XYItemRenderer r = getRenderer(i);
2817:                 if (i >= getDatasetCount()) { // we need the dataset to make
2818:                     continue;                 // a link to the axes
2819:                 }
2820:                 if (r != null) {
2821:                     ValueAxis domainAxis = getDomainAxisForDataset(i);
2822:                     ValueAxis rangeAxis = getRangeAxisForDataset(i);
2823:                     r.drawAnnotations(g2, dataArea, domainAxis, rangeAxis,
2824:                             Layer.FOREGROUND, info);
2825:                 }
2826:             }
2827: 
2828:         }
2829: 
2830:         // draw domain crosshair if required...
2831:         int xAxisIndex = crosshairState.getDomainAxisIndex();
2832:         ValueAxis xAxis = getDomainAxis(xAxisIndex);
2833:         RectangleEdge xAxisEdge = getDomainAxisEdge(xAxisIndex);
2834:         if (!this.domainCrosshairLockedOnData && anchor != null) {
2835:             double xx;
2836:             if (orient == PlotOrientation.VERTICAL) {
2837:                 xx = xAxis.java2DToValue(anchor.getX(), dataArea, xAxisEdge);
2838:             } 
2839:             else {
2840:                 xx = xAxis.java2DToValue(anchor.getY(), dataArea, xAxisEdge);
2841:             }
2842:             crosshairState.setCrosshairX(xx);
2843:         }
2844:         setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
2845:         if (isDomainCrosshairVisible()) {
2846:             double x = getDomainCrosshairValue();
2847:             Paint paint = getDomainCrosshairPaint();
2848:             Stroke stroke = getDomainCrosshairStroke();
2849:             drawDomainCrosshair(g2, dataArea, orient, x, xAxis, stroke, paint);
2850:         }
2851: 
2852:         // draw range crosshair if required...
2853:         int yAxisIndex = crosshairState.getRangeAxisIndex();
2854:         ValueAxis yAxis = getRangeAxis(yAxisIndex);
2855:         RectangleEdge yAxisEdge = getRangeAxisEdge(yAxisIndex);
2856:         if (!this.rangeCrosshairLockedOnData && anchor != null) {
2857:             double yy;
2858:             if (orient == PlotOrientation.VERTICAL) {
2859:                 yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge);
2860:             } else {
2861:                 yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge);
2862:             }
2863:             crosshairState.setCrosshairY(yy);
2864:         }
2865:         setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
2866:         if (isRangeCrosshairVisible()) {
2867:             double y = getRangeCrosshairValue();
2868:             Paint paint = getRangeCrosshairPaint();
2869:             Stroke stroke = getRangeCrosshairStroke();
2870:             drawRangeCrosshair(g2, dataArea, orient, y, yAxis, stroke, paint);
2871:         }
2872: 
2873:         if (!foundData) {
2874:             drawNoDataMessage(g2, dataArea);
2875:         }
2876: 
2877:         for (int i = 0; i < this.renderers.size(); i++) {
2878:             drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2879:         }
2880:         for (int i = 0; i < this.renderers.size(); i++) {
2881:             drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2882:         }
2883: 
2884:         drawAnnotations(g2, dataArea, info);
2885:         g2.setClip(originalClip);
2886:         g2.setComposite(originalComposite);
2887: 
2888:         drawOutline(g2, dataArea);
2889: 
2890:     }
2891: 
2892:     /**
2893:      * Draws the background for the plot.
2894:      *
2895:      * @param g2  the graphics device.
2896:      * @param area  the area.
2897:      */
2898:     public void drawBackground(Graphics2D g2, Rectangle2D area) {
2899:         fillBackground(g2, area, this.orientation);
2900:         drawQuadrants(g2, area);
2901:         drawBackgroundImage(g2, area);
2902:     }
2903: 
2904:     /**
2905:      * Draws the quadrants.
2906:      *
2907:      * @param g2  the graphics device.
2908:      * @param area  the area.
2909:      * 
2910:      * @see #setQuadrantOrigin(Point2D)
2911:      * @see #setQuadrantPaint(int, Paint)
2912:      */
2913:     protected void drawQuadrants(Graphics2D g2, Rectangle2D area) {
2914:         //  0 | 1
2915:         //  --+--
2916:         //  2 | 3
2917:         boolean somethingToDraw = false;
2918: 
2919:         ValueAxis xAxis = getDomainAxis();
2920:         double x = xAxis.getRange().constrain(this.quadrantOrigin.getX());
2921:         double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge());
2922: 
2923:         ValueAxis yAxis = getRangeAxis();
2924:         double y = yAxis.getRange().constrain(this.quadrantOrigin.getY());
2925:         double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge());
2926: 
2927:         double xmin = xAxis.getLowerBound();
2928:         double xxmin = xAxis.valueToJava2D(xmin, area, getDomainAxisEdge());
2929: 
2930:         double xmax = xAxis.getUpperBound();
2931:         double xxmax = xAxis.valueToJava2D(xmax, area, getDomainAxisEdge());
2932: 
2933:         double ymin = yAxis.getLowerBound();
2934:         double yymin = yAxis.valueToJava2D(ymin, area, getRangeAxisEdge());
2935: 
2936:         double ymax = yAxis.getUpperBound();
2937:         double yymax = yAxis.valueToJava2D(ymax, area, getRangeAxisEdge());
2938: 
2939:         Rectangle2D[] r = new Rectangle2D[] {null, null, null, null};
2940:         if (this.quadrantPaint[0] != null) {
2941:             if (x > xmin && y < ymax) {
2942:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2943:                     r[0] = new Rectangle2D.Double(Math.min(yymax, yy), 
2944:                             Math.min(xxmin, xx), Math.abs(yy - yymax), 
2945:                             Math.abs(xx - xxmin)
2946:                     );
2947:                 }
2948:                 else {  // PlotOrientation.VERTICAL
2949:                     r[0] = new Rectangle2D.Double(Math.min(xxmin, xx), 
2950:                             Math.min(yymax, yy), Math.abs(xx - xxmin), 
2951:                             Math.abs(yy - yymax));
2952:                 }
2953:                 somethingToDraw = true;
2954:             }
2955:         }
2956:         if (this.quadrantPaint[1] != null) {
2957:             if (x < xmax && y < ymax) {
2958:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2959:                     r[1] = new Rectangle2D.Double(Math.min(yymax, yy), 
2960:                             Math.min(xxmax, xx), Math.abs(yy - yymax), 
2961:                             Math.abs(xx - xxmax));
2962:                 }
2963:                 else {  // PlotOrientation.VERTICAL
2964:                     r[1] = new Rectangle2D.Double(Math.min(xx, xxmax), 
2965:                             Math.min(yymax, yy), Math.abs(xx - xxmax), 
2966:                             Math.abs(yy - yymax));
2967:                 }
2968:                 somethingToDraw = true;
2969:             }
2970:         }
2971:         if (this.quadrantPaint[2] != null) {
2972:             if (x > xmin && y > ymin) {
2973:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2974:                     r[2] = new Rectangle2D.Double(Math.min(yymin, yy), 
2975:                             Math.min(xxmin, xx), Math.abs(yy - yymin), 
2976:                             Math.abs(xx - xxmin));
2977:                 }
2978:                 else {  // PlotOrientation.VERTICAL
2979:                     r[2] = new Rectangle2D.Double(Math.min(xxmin, xx), 
2980:                             Math.min(yymin, yy), Math.abs(xx - xxmin), 
2981:                             Math.abs(yy - yymin));
2982:                 }
2983:                 somethingToDraw = true;
2984:             }
2985:         }
2986:         if (this.quadrantPaint[3] != null) {
2987:             if (x < xmax && y > ymin) {
2988:                 if (this.orientation == PlotOrientation.HORIZONTAL) {
2989:                     r[3] = new Rectangle2D.Double(Math.min(yymin, yy), 
2990:                             Math.min(xxmax, xx), Math.abs(yy - yymin), 
2991:                             Math.abs(xx - xxmax));
2992:                 }
2993:                 else {  // PlotOrientation.VERTICAL
2994:                     r[3] = new Rectangle2D.Double(Math.min(xx, xxmax), 
2995:                             Math.min(yymin, yy), Math.abs(xx - xxmax), 
2996:                             Math.abs(yy - yymin));
2997:                 }
2998:                 somethingToDraw = true;
2999:             }
3000:         }
3001:         if (somethingToDraw) {
3002:             Composite originalComposite = g2.getComposite();
3003:             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
3004:                     getBackgroundAlpha()));
3005:             for (int i = 0; i < 4; i++) {
3006:                 if (this.quadrantPaint[i] != null && r[i] != null) {
3007:                     g2.setPaint(this.quadrantPaint[i]);
3008:                     g2.fill(r[i]);
3009:                 }
3010:             }
3011:             g2.setComposite(originalComposite);
3012:         }
3013:     }
3014: 
3015:     /**
3016:      * Draws the domain tick bands, if any.
3017:      *
3018:      * @param g2  the graphics device.
3019:      * @param dataArea  the data area.
3020:      * @param ticks  the ticks.
3021:      * 
3022:      * @see #setDomainTickBandPaint(Paint)
3023:      */
3024:     public void drawDomainTickBands(Graphics2D g2, Rectangle2D dataArea,
3025:                                     List ticks) {
3026:         Paint bandPaint = getDomainTickBandPaint();
3027:         if (bandPaint != null) {
3028:             boolean fillBand = false;
3029:             ValueAxis xAxis = getDomainAxis();
3030:             double previous = xAxis.getLowerBound();
3031:             Iterator iterator = ticks.iterator();
3032:             while (iterator.hasNext()) {
3033:                 ValueTick tick = (ValueTick) iterator.next();
3034:                 double current = tick.getValue();
3035:                 if (fillBand) {
3036:                     getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea,
3037:                             previous, current);
3038:                 }
3039:                 previous = current;
3040:                 fillBand = !fillBand;
3041:             }
3042:             double end = xAxis.getUpperBound();
3043:             if (fillBand) {
3044:                 getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea, 
3045:                         previous, end);
3046:             }
3047:         }
3048:     }
3049: 
3050:     /**
3051:      * Draws the range tick bands, if any.
3052:      *
3053:      * @param g2  the graphics device.
3054:      * @param dataArea  the data area.
3055:      * @param ticks  the ticks.
3056:      * 
3057:      * @see #setRangeTickBandPaint(Paint)
3058:      */
3059:     public void drawRangeTickBands(Graphics2D g2, Rectangle2D dataArea,
3060:                                    List ticks) {
3061:         Paint bandPaint = getRangeTickBandPaint();
3062:         if (bandPaint != null) {
3063:             boolean fillBand = false;
3064:             ValueAxis axis = getRangeAxis();
3065:             double previous = axis.getLowerBound();
3066:             Iterator iterator = ticks.iterator();
3067:             while (iterator.hasNext()) {
3068:                 ValueTick tick = (ValueTick) iterator.next();
3069:                 double current = tick.getValue();
3070:                 if (fillBand) {
3071:                     getRenderer().fillRangeGridBand(g2, this, axis, dataArea, 
3072:                             previous, current);
3073:                 }
3074:                 previous = current;
3075:                 fillBand = !fillBand;
3076:             }
3077:             double end = axis.getUpperBound();
3078:             if (fillBand) {
3079:                 getRenderer().fillRangeGridBand(g2, this, axis, dataArea, 
3080:                         previous, end);
3081:             }
3082:         }
3083:     }
3084: 
3085:     /**
3086:      * A utility method for drawing the axes.
3087:      *
3088:      * @param g2  the graphics device (<code>null</code> not permitted).
3089:      * @param plotArea  the plot area (<code>null</code> not permitted).
3090:      * @param dataArea  the data area (<code>null</code> not permitted).
3091:      * @param plotState  collects information about the plot (<code>null</code>
3092:      *                   permitted).
3093:      *
3094:      * @return A map containing the state for each axis drawn.
3095:      */
3096:     protected Map drawAxes(Graphics2D g2,
3097:                            Rectangle2D plotArea,
3098:                            Rectangle2D dataArea,
3099:                            PlotRenderingInfo plotState) {
3100: 
3101:         AxisCollection axisCollection = new AxisCollection();
3102: 
3103:         // add domain axes to lists...
3104:         for (int index = 0; index < this.domainAxes.size(); index++) {
3105:             ValueAxis axis = (ValueAxis) this.domainAxes.get(index);
3106:             if (axis != null) {
3107:                 axisCollection.add(axis, getDomainAxisEdge(index));
3108:             }
3109:         }
3110: 
3111:         // add range axes to lists...
3112:         for (int index = 0; index < this.rangeAxes.size(); index++) {
3113:             ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index);
3114:             if (yAxis != null) {
3115:                 axisCollection.add(yAxis, getRangeAxisEdge(index));
3116:             }
3117:         }
3118: 
3119:         Map axisStateMap = new HashMap();
3120: 
3121:         // draw the top axes
3122:         double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(
3123:                 dataArea.getHeight());
3124:         Iterator iterator = axisCollection.getAxesAtTop().iterator();
3125:         while (iterator.hasNext()) {
3126:             ValueAxis axis = (ValueAxis) iterator.next();
3127:             AxisState info = axis.draw(g2, cursor, plotArea, dataArea, 
3128:                     RectangleEdge.TOP, plotState);
3129:             cursor = info.getCursor();
3130:             axisStateMap.put(axis, info);
3131:         }
3132: 
3133:         // draw the bottom axes
3134:         cursor = dataArea.getMaxY()
3135:                  + this.axisOffset.calculateBottomOutset(dataArea.getHeight());
3136:         iterator = axisCollection.getAxesAtBottom().iterator();
3137:         while (iterator.hasNext()) {
3138:             ValueAxis axis = (ValueAxis) iterator.next();
3139:             AxisState info = axis.draw(g2, cursor, plotArea, dataArea, 
3140:                     RectangleEdge.BOTTOM, plotState);
3141:             cursor = info.getCursor();
3142:             axisStateMap.put(axis, info);
3143:         }
3144: 
3145:         // draw the left axes
3146:         cursor = dataArea.getMinX()
3147:                  - this.axisOffset.calculateLeftOutset(dataArea.getWidth());
3148:         iterator = axisCollection.getAxesAtLeft().iterator();
3149:         while (iterator.hasNext()) {
3150:             ValueAxis axis = (ValueAxis) iterator.next();
3151:             AxisState info = axis.draw(g2, cursor, plotArea, dataArea, 
3152:                     RectangleEdge.LEFT, plotState);
3153:             cursor = info.getCursor();
3154:             axisStateMap.put(axis, info);
3155:         }
3156: 
3157:         // draw the right axes
3158:         cursor = dataArea.getMaxX()
3159:                  + this.axisOffset.calculateRightOutset(dataArea.getWidth());
3160:         iterator = axisCollection.getAxesAtRight().iterator();
3161:         while (iterator.hasNext()) {
3162:             ValueAxis axis = (ValueAxis) iterator.next();
3163:             AxisState info = axis.draw(g2, cursor, plotArea, dataArea, 
3164:                     RectangleEdge.RIGHT, plotState);
3165:             cursor = info.getCursor();
3166:             axisStateMap.put(axis, info);
3167:         }
3168: 
3169:         return axisStateMap;
3170:     }
3171: 
3172:     /**
3173:      * Draws a representation of the data within the dataArea region, using the
3174:      * current renderer.
3175:      * <P>
3176:      * The <code>info</code> and <code>crosshairState</code> arguments may be
3177:      * <code>null</code>.
3178:      *
3179:      * @param g2  the graphics device.
3180:      * @param dataArea  the region in which the data is to be drawn.
3181:      * @param index  the dataset index.
3182:      * @param info  an optional object for collection dimension information.
3183:      * @param crosshairState  collects crosshair information
3184:      *                        (<code>null</code> permitted).
3185:      *
3186:      * @return A flag that indicates whether any data was actually rendered.
3187:      */
3188:     public boolean render(Graphics2D g2,
3189:                           Rectangle2D dataArea,
3190:                           int index,
3191:                           PlotRenderingInfo info,
3192:                           CrosshairState crosshairState) {
3193: 
3194:         boolean foundData = false;
3195:         XYDataset dataset = getDataset(index);
3196:         if (!DatasetUtilities.isEmptyOrNull(dataset)) {
3197:             foundData = true;
3198:             ValueAxis xAxis = getDomainAxisForDataset(index);
3199:             ValueAxis yAxis = getRangeAxisForDataset(index);
3200:             XYItemRenderer renderer = getRenderer(index);
3201:             if (renderer == null) {
3202:                 renderer = getRenderer();
3203:                 if (renderer == null) { // no default renderer available
3204:                     return foundData;
3205:                 }
3206:             }
3207: 
3208:             XYItemRendererState state = renderer.initialise(g2, dataArea, this,
3209:                     dataset, info);
3210:             int passCount = renderer.getPassCount();
3211: 
3212:             SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
3213:             if (seriesOrder == SeriesRenderingOrder.REVERSE) {
3214:                 //render series in reverse order
3215:                 for (int pass = 0; pass < passCount; pass++) {
3216:                     int seriesCount = dataset.getSeriesCount();
3217:                     for (int series = seriesCount - 1; series >= 0; series--) {
3218:                         int firstItem = 0;
3219:                         int lastItem = dataset.getItemCount(series) - 1;
3220:                         if (lastItem == -1) {
3221:                             continue;
3222:                         }
3223:                         if (state.getProcessVisibleItemsOnly()) {
3224:                             int[] itemBounds = RendererUtilities.findLiveItems(
3225:                                     dataset, series, xAxis.getLowerBound(), 
3226:                                     xAxis.getUpperBound());
3227:                             firstItem = itemBounds[0];
3228:                             lastItem = itemBounds[1];
3229:                         }
3230:                         for (int item = firstItem; item <= lastItem; item++) {
3231:                             renderer.drawItem(g2, state, dataArea, info,
3232:                                     this, xAxis, yAxis, dataset, series, item,
3233:                                     crosshairState, pass);
3234:                         }
3235:                     }
3236:                 }
3237:             }
3238:             else {
3239:                 //render series in forward order
3240:                 for (int pass = 0; pass < passCount; pass++) {
3241:                     int seriesCount = dataset.getSeriesCount();
3242:                     for (int series = 0; series < seriesCount; series++) {
3243:                         int firstItem = 0;
3244:                         int lastItem = dataset.getItemCount(series) - 1;
3245:                         if (state.getProcessVisibleItemsOnly()) {
3246:                             int[] itemBounds = RendererUtilities.findLiveItems(
3247:                                     dataset, series, xAxis.getLowerBound(), 
3248:                                     xAxis.getUpperBound());
3249:                             firstItem = itemBounds[0];
3250:                             lastItem = itemBounds[1];
3251:                         }
3252:                         for (int item = firstItem; item <= lastItem; item++) {
3253:                             renderer.drawItem(g2, state, dataArea, info,
3254:                                     this, xAxis, yAxis, dataset, series, item,
3255:                                     crosshairState, pass);
3256:                         }
3257:                     }
3258:                 }
3259:             }
3260:         }
3261:         return foundData;
3262:     }
3263: 
3264:     /**
3265:      * Returns the domain axis for a dataset.
3266:      *
3267:      * @param index  the dataset index.
3268:      *
3269:      * @return The axis.
3270:      */
3271:     public ValueAxis getDomainAxisForDataset(int index) {
3272: 
3273:         if (index < 0 || index >= getDatasetCount()) {
3274:             throw new IllegalArgumentException("Index " + index 
3275:                     + " out of bounds.");
3276:         }
3277: 
3278:         ValueAxis valueAxis = null;
3279:         Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(
3280:                 new Integer(index));
3281:         if (axisIndex != null) {
3282:             valueAxis = getDomainAxis(axisIndex.intValue());
3283:         }
3284:         else {
3285:             valueAxis = getDomainAxis(0);
3286:         }
3287:         return valueAxis;
3288: 
3289:     }
3290: 
3291:     /**
3292:      * Returns the range axis for a dataset.
3293:      *
3294:      * @param index  the dataset index.
3295:      *
3296:      * @return The axis.
3297:      */
3298:     public ValueAxis getRangeAxisForDataset(int index) {
3299: 
3300:         if (index < 0 || index >= getDatasetCount()) {
3301:             throw new IllegalArgumentException("Index " + index 
3302:                     + " out of bounds.");
3303:         }
3304: 
3305:         ValueAxis valueAxis = null;
3306:         Integer axisIndex
3307:             = (Integer) this.datasetToRangeAxisMap.get(new Integer(index));
3308:         if (axisIndex != null) {
3309:             valueAxis = getRangeAxis(axisIndex.intValue());
3310:         }
3311:         else {
3312:             valueAxis = getRangeAxis(0);
3313:         }
3314:         return valueAxis;
3315: 
3316:     }
3317: 
3318:     /**
3319:      * Draws the gridlines for the plot, if they are visible.
3320:      *
3321:      * @param g2  the graphics device.
3322:      * @param dataArea  the data area.
3323:      * @param ticks  the ticks.
3324:      * 
3325:      * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List)
3326:      */
3327:     protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea,
3328:                                        List ticks) {
3329: 
3330:         // no renderer, no gridlines...
3331:         if (getRenderer() == null) {
3332:             return;
3333:         }
3334: 
3335:         // draw the domain grid lines, if any...
3336:         if (isDomainGridlinesVisible()) {
3337:             Stroke gridStroke = getDomainGridlineStroke();
3338:             Paint gridPaint = getDomainGridlinePaint();
3339:             if ((gridStroke != null) && (gridPaint != null)) {
3340:                 Iterator iterator = ticks.iterator();
3341:                 while (iterator.hasNext()) {
3342:                     ValueTick tick = (ValueTick) iterator.next();
3343:                     getRenderer().drawDomainGridLine(g2, this, getDomainAxis(),
3344:                             dataArea, tick.getValue());
3345:                 }
3346:             }
3347:         }
3348:     }
3349: 
3350:     /**
3351:      * Draws the gridlines for the plot's primary range axis, if they are
3352:      * visible.
3353:      *
3354:      * @param g2  the graphics device.
3355:      * @param area  the data area.
3356:      * @param ticks  the ticks.
3357:      * 
3358:      * @see #drawDomainGridlines(Graphics2D, Rectangle2D, List)
3359:      */
3360:     protected void drawRangeGridlines(Graphics2D g2, Rectangle2D area,
3361:                                       List ticks) {
3362: 
3363:         // no renderer, no gridlines...
3364:         if (getRenderer() == null) {
3365:             return;
3366:         }
3367: 
3368:         // draw the range grid lines, if any...
3369:         if (isRangeGridlinesVisible()) {
3370:             Stroke gridStroke = getRangeGridlineStroke();
3371:             Paint gridPaint = getRangeGridlinePaint();
3372:             ValueAxis axis = getRangeAxis();
3373:             if (axis != null) {
3374:                 Iterator iterator = ticks.iterator();
3375:                 while (iterator.hasNext()) {
3376:                     ValueTick tick = (ValueTick) iterator.next();
3377:                     if (tick.getValue() != 0.0
3378:                             || !isRangeZeroBaselineVisible()) {
3379:                         getRenderer().drawRangeLine(g2, this, getRangeAxis(), 
3380:                                 area, tick.getValue(), gridPaint, gridStroke);
3381:                     }
3382:                 }
3383:             }
3384:         }
3385:     }
3386: 
3387:     /**
3388:      * Draws a base line across the chart at value zero on the domain axis.
3389:      *
3390:      * @param g2  the graphics device.
3391:      * @param area  the data area.
3392:      * 
3393:      * @see #setDomainZeroBaselineVisible(boolean)
3394:      * 
3395:      * @since 1.0.5
3396:      */
3397:     protected void drawZeroDomainBaseline(Graphics2D g2, Rectangle2D area) {
3398:         if (isDomainZeroBaselineVisible()) {
3399:             XYItemRenderer r = getRenderer();
3400:             // FIXME: the renderer interface doesn't have the drawDomainLine()
3401:             // method, so we have to rely on the renderer being a subclass of
3402:             // AbstractXYItemRenderer (which is lame)
3403:             if (r instanceof AbstractXYItemRenderer) {
3404:                 AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) r;
3405:                 renderer.drawDomainLine(g2, this, getDomainAxis(), area, 0.0, 
3406:                         this.domainZeroBaselinePaint, 
3407:                         this.domainZeroBaselineStroke);
3408:             }
3409:         }
3410:     }
3411: 
3412:     /**
3413:      * Draws a base line across the chart at value zero on the range axis.
3414:      *
3415:      * @param g2  the graphics device.
3416:      * @param area  the data area.
3417:      * 
3418:      * @see #setRangeZeroBaselineVisible(boolean)
3419:      */
3420:     protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) {
3421:         if (isRangeZeroBaselineVisible()) {
3422:             getRenderer().drawRangeLine(g2, this, getRangeAxis(), area, 0.0, 
3423:                     this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke);
3424:         }
3425:     }
3426: 
3427:     /**
3428:      * Draws the annotations for the plot.
3429:      *
3430:      * @param g2  the graphics device.
3431:      * @param dataArea  the data area.
3432:      * @param info  the chart rendering info.
3433:      */
3434:     public void drawAnnotations(Graphics2D g2,
3435:                                 Rectangle2D dataArea,
3436:                                 PlotRenderingInfo info) {
3437: 
3438:         Iterator iterator = this.annotations.iterator();
3439:         while (iterator.hasNext()) {
3440:             XYAnnotation annotation = (XYAnnotation) iterator.next();
3441:             ValueAxis xAxis = getDomainAxis();
3442:             ValueAxis yAxis = getRangeAxis();
3443:             annotation.draw(g2, this, dataArea, xAxis, yAxis, 0, info);
3444:         }
3445: 
3446:     }
3447: 
3448:     /**
3449:      * Draws the domain markers (if any) for an axis and layer.  This method is
3450:      * typically called from within the draw() method.
3451:      *
3452:      * @param g2  the graphics device.
3453:      * @param dataArea  the data area.
3454:      * @param index  the renderer index.
3455:      * @param layer  the layer (foreground or background).
3456:      */
3457:     protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea,
3458:                                      int index, Layer layer) {
3459: 
3460:         XYItemRenderer r = getRenderer(index);
3461:         if (r == null) {
3462:             return;
3463:         }
3464:         // check that the renderer has a corresponding dataset (it doesn't
3465:         // matter if the dataset is null)
3466:         if (index >= getDatasetCount()) {
3467:             return;
3468:         }    
3469:         Collection markers = getDomainMarkers(index, layer);
3470:         ValueAxis axis = getDomainAxisForDataset(index);
3471:         if (markers != null && axis != null) {
3472:             Iterator iterator = markers.iterator();
3473:             while (iterator.hasNext()) {
3474:                 Marker marker = (Marker) iterator.next();
3475:                 r.drawDomainMarker(g2, this, axis, marker, dataArea);
3476:             }
3477:         }
3478: 
3479:     }
3480: 
3481:     /**
3482:      * Draws the range markers (if any) for a renderer and layer.  This method
3483:      * is typically called from within the draw() method.
3484:      *
3485:      * @param g2  the graphics device.
3486:      * @param dataArea  the data area.
3487:      * @param index  the renderer index.
3488:      * @param layer  the layer (foreground or background).
3489:      */
3490:     protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea,
3491:                                     int index, Layer layer) {
3492: 
3493:         XYItemRenderer r = getRenderer(index);
3494:         if (r == null) {
3495:             return;
3496:         }
3497:         // check that the renderer has a corresponding dataset (it doesn't
3498:         // matter if the dataset is null)
3499:         if (index >= getDatasetCount()) {
3500:             return;
3501:         }
3502:         Collection markers = getRangeMarkers(index, layer);
3503:         ValueAxis axis = getRangeAxisForDataset(index);
3504:         if (markers != null && axis != null) {
3505:             Iterator iterator = markers.iterator();
3506:             while (iterator.hasNext()) {
3507:                 Marker marker = (Marker) iterator.next();
3508:                 r.drawRangeMarker(g2, this, axis, marker, dataArea);
3509:             }
3510:         }
3511:     }
3512: 
3513:     /**
3514:      * Returns the list of domain markers (read only) for the specified layer.
3515:      *
3516:      * @param layer  the layer (foreground or background).
3517:      *
3518:      * @return The list of domain markers.
3519:      * 
3520:      * @see #getRangeMarkers(Layer)
3521:      */
3522:     public Collection getDomainMarkers(Layer layer) {
3523:         return getDomainMarkers(0, layer);
3524:     }
3525: 
3526:     /**
3527:      * Returns the list of range markers (read only) for the specified layer.
3528:      *
3529:      * @param layer  the layer (foreground or background).
3530:      *
3531:      * @return The list of range markers.
3532:      * 
3533:      * @see #getDomainMarkers(Layer)
3534:      */
3535:     public Collection getRangeMarkers(Layer layer) {
3536:         return getRangeMarkers(0, layer);
3537:     }
3538: 
3539:     /**
3540:      * Returns a collection of domain markers for a particular renderer and
3541:      * layer.
3542:      *
3543:      * @param index  the renderer index.
3544:      * @param layer  the layer.
3545:      *
3546:      * @return A collection of markers (possibly <code>null</code>).
3547:      * 
3548:      * @see #getRangeMarkers(int, Layer)
3549:      */
3550:     public Collection getDomainMarkers(int index, Layer layer) {
3551:         Collection result = null;
3552:         Integer key = new Integer(index);
3553:         if (layer == Layer.FOREGROUND) {
3554:             result = (Collection) this.foregroundDomainMarkers.get(key);
3555:         }
3556:         else if (layer == Layer.BACKGROUND) {
3557:             result = (Collection) this.backgroundDomainMarkers.get(key);
3558:         }
3559:         if (result != null) {
3560:             result = Collections.unmodifiableCollection(result);
3561:         }
3562:         return result;
3563:     }
3564: 
3565:     /**
3566:      * Returns a collection of range markers for a particular renderer and
3567:      * layer.
3568:      *
3569:      * @param index  the renderer index.
3570:      * @param layer  the layer.
3571:      *
3572:      * @return A collection of markers (possibly <code>null</code>).
3573:      * 
3574:      * @see #getDomainMarkers(int, Layer)
3575:      */
3576:     public Collection getRangeMarkers(int index, Layer layer) {
3577:         Collection result = null;
3578:         Integer key = new Integer(index);
3579:         if (layer == Layer.FOREGROUND) {
3580:             result = (Collection) this.foregroundRangeMarkers.get(key);
3581:         }
3582:         else if (layer == Layer.BACKGROUND) {
3583:             result = (Collection) this.backgroundRangeMarkers.get(key);
3584:         }
3585:         if (result != null) {
3586:             result = Collections.unmodifiableCollection(result);
3587:         }
3588:         return result;
3589:     }
3590: 
3591:     /**
3592:      * Utility method for drawing a horizontal line across the data area of the
3593:      * plot.
3594:      *
3595:      * @param g2  the graphics device.
3596:      * @param dataArea  the data area.
3597:      * @param value  the coordinate, where to draw the line.
3598:      * @param stroke  the stroke to use.
3599:      * @param paint  the paint to use.
3600:      */
3601:     protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea,
3602:                                       double value, Stroke stroke,
3603:                                       Paint paint) {
3604: 
3605:         ValueAxis axis = getRangeAxis();
3606:         if (getOrientation() == PlotOrientation.HORIZONTAL) {
3607:             axis = getDomainAxis();
3608:         }
3609:         if (axis.getRange().contains(value)) {
3610:             double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT);
3611:             Line2D line = new Line2D.Double(dataArea.getMinX(), yy, 
3612:                     dataArea.getMaxX(), yy);
3613:             g2.setStroke(stroke);
3614:             g2.setPaint(paint);
3615:             g2.draw(line);
3616:         }
3617: 
3618:     }
3619:     
3620:     /**
3621:      * Draws a domain crosshair.
3622:      * 
3623:      * @param g2  the graphics target.
3624:      * @param dataArea  the data area.
3625:      * @param orientation  the plot orientation.
3626:      * @param value  the crosshair value.
3627:      * @param axis  the axis against which the value is measured.
3628:      * @param stroke  the stroke used to draw the crosshair line.
3629:      * @param paint  the paint used to draw the crosshair line.
3630:      * 
3631:      * @since 1.0.4
3632:      */
3633:     protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea, 
3634:             PlotOrientation orientation, double value, ValueAxis axis, 
3635:             Stroke stroke, Paint paint) {
3636:         
3637:         if (axis.getRange().contains(value)) {
3638:             Line2D line = null;
3639:             if (orientation == PlotOrientation.VERTICAL) {
3640:                 double xx = axis.valueToJava2D(value, dataArea, 
3641:                         RectangleEdge.BOTTOM);
3642:                 line = new Line2D.Double(xx, dataArea.getMinY(), xx, 
3643:                         dataArea.getMaxY());
3644:             }
3645:             else {
3646:                 double yy = axis.valueToJava2D(value, dataArea, 
3647:                         RectangleEdge.LEFT);
3648:                 line = new Line2D.Double(dataArea.getMinX(), yy, 
3649:                         dataArea.getMaxX(), yy);
3650:             }
3651:             g2.setStroke(stroke);
3652:             g2.setPaint(paint);
3653:             g2.draw(line);
3654:         }
3655:         
3656:     }
3657: 
3658:     /**
3659:      * Utility method for drawing a vertical line on the data area of the plot.
3660:      *
3661:      * @param g2  the graphics device.
3662:      * @param dataArea  the data area.
3663:      * @param value  the coordinate, where to draw the line.
3664:      * @param stroke  the stroke to use.
3665:      * @param paint  the paint to use.
3666:      */
3667:     protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea,
3668:                                     double value, Stroke stroke, Paint paint) {
3669: 
3670:         ValueAxis axis = getDomainAxis();
3671:         if (getOrientation() == PlotOrientation.HORIZONTAL) {
3672:             axis = getRangeAxis();
3673:         }
3674:         if (axis.getRange().contains(value)) {
3675:             double xx = axis.valueToJava2D(value, dataArea, 
3676:                     RectangleEdge.BOTTOM);
3677:             Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, 
3678:                     dataArea.getMaxY());
3679:             g2.setStroke(stroke);
3680:             g2.setPaint(paint);
3681:             g2.draw(line);
3682:         }
3683: 
3684:     }
3685: 
3686:     /**
3687:      * Draws a range crosshair.
3688:      * 
3689:      * @param g2  the graphics target.
3690:      * @param dataArea  the data area.
3691:      * @param orientation  the plot orientation.
3692:      * @param value  the crosshair value.
3693:      * @param axis  the axis against which the value is measured.
3694:      * @param stroke  the stroke used to draw the crosshair line.
3695:      * @param paint  the paint used to draw the crosshair line.
3696:      * 
3697:      * @since 1.0.4
3698:      */
3699:     protected void drawRangeCrosshair(Graphics2D g2, Rectangle2D dataArea, 
3700:             PlotOrientation orientation, double value, ValueAxis axis, 
3701:             Stroke stroke, Paint paint) {
3702:         
3703:         if (axis.getRange().contains(value)) {
3704:             Line2D line = null;
3705:             if (orientation == PlotOrientation.HORIZONTAL) {
3706:                 double xx = axis.valueToJava2D(value, dataArea, 
3707:                         RectangleEdge.BOTTOM);
3708:                 line = new Line2D.Double(xx, dataArea.getMinY(), xx, 
3709:                         dataArea.getMaxY());
3710:             }
3711:             else {
3712:                 double yy = axis.valueToJava2D(value, dataArea, 
3713:                         RectangleEdge.LEFT);
3714:                 line = new Line2D.Double(dataArea.getMinX(), yy, 
3715:                         dataArea.getMaxX(), yy);
3716:             }
3717:             g2.setStroke(stroke);
3718:             g2.setPaint(paint);
3719:             g2.draw(line);
3720:         }
3721:         
3722:     }
3723: 
3724:     /**
3725:      * Handles a 'click' on the plot by updating the anchor values.
3726:      *
3727:      * @param x  the x-coordinate, where the click occurred, in Java2D space.
3728:      * @param y  the y-coordinate, where the click occurred, in Java2D space.
3729:      * @param info  object containing information about the plot dimensions.
3730:      */
3731:     public void handleClick(int x, int y, PlotRenderingInfo info) {
3732: 
3733:         Rectangle2D dataArea = info.getDataArea();
3734:         if (dataArea.contains(x, y)) {
3735:             // set the anchor value for the horizontal axis...
3736:             ValueAxis da = getDomainAxis();
3737:             if (da != null) {
3738:                 double hvalue = da.java2DToValue(x, info.getDataArea(), 
3739:                         getDomainAxisEdge());
3740:                 setDomainCrosshairValue(hvalue);
3741:             }
3742: 
3743:             // set the anchor value for the vertical axis...
3744:             ValueAxis ra = getRangeAxis();
3745:             if (ra != null) {
3746:                 double vvalue = ra.java2DToValue(y, info.getDataArea(), 
3747:                         getRangeAxisEdge());
3748:                 setRangeCrosshairValue(vvalue);
3749:             }
3750:         }
3751:     }
3752: 
3753:     /**
3754:      * A utility method that returns a list of datasets that are mapped to a
3755:      * particular axis.
3756:      *
3757:      * @param axisIndex  the axis index (<code>null</code> not permitted).
3758:      *
3759:      * @return A list of datasets.
3760:      */
3761:     private List getDatasetsMappedToDomainAxis(Integer axisIndex) {
3762:         if (axisIndex == null) {
3763:             throw new IllegalArgumentException("Null 'axisIndex' argument.");
3764:         }
3765:         List result = new ArrayList();
3766:         for (int i = 0; i < this.datasets.size(); i++) {
3767:             Integer mappedAxis = (Integer) this.datasetToDomainAxisMap.get(
3768:                     new Integer(i));
3769:             if (mappedAxis == null) {
3770:                 if (axisIndex.equals(ZERO)) {
3771:                     result.add(this.datasets.get(i));
3772:                 }
3773:             }
3774:             else {
3775:                 if (mappedAxis.equals(axisIndex)) {
3776:                     result.add(this.datasets.get(i));
3777:                 }
3778:             }
3779:         }
3780:         return result;
3781:     }
3782: 
3783:     /**
3784:      * A utility method that returns a list of datasets that are mapped to a
3785:      * particular axis.
3786:      *
3787:      * @param axisIndex  the axis index (<code>null</code> not permitted).
3788:      *
3789:      * @return A list of datasets.
3790:      */
3791:     private List getDatasetsMappedToRangeAxis(Integer axisIndex) {
3792:         if (axisIndex == null) {
3793:             throw new IllegalArgumentException("Null 'axisIndex' argument.");
3794:         }
3795:         List result = new ArrayList();
3796:         for (int i = 0; i < this.datasets.size(); i++) {
3797:             Integer mappedAxis = (Integer) this.datasetToRangeAxisMap.get(
3798:                     new Integer(i));
3799:             if (mappedAxis == null) {
3800:                 if (axisIndex.equals(ZERO)) {
3801:                     result.add(this.datasets.get(i));
3802:                 }
3803:             }
3804:             else {
3805:                 if (mappedAxis.equals(axisIndex)) {
3806:                     result.add(this.datasets.get(i));
3807:                 }
3808:             }
3809:         }
3810:         return result;
3811:     }
3812: 
3813:     /**
3814:      * Returns the index of the given domain axis.
3815:      *
3816:      * @param axis  the axis.
3817:      *
3818:      * @return The axis index.
3819:      * 
3820:      * @see #getRangeAxisIndex(ValueAxis)
3821:      */
3822:     public int getDomainAxisIndex(ValueAxis axis) {
3823:         int result = this.domainAxes.indexOf(axis);
3824:         if (result < 0) {
3825:             // try the parent plot
3826:             Plot parent = getParent();
3827:             if (parent instanceof XYPlot) {
3828:                 XYPlot p = (XYPlot) parent;
3829:                 result = p.getDomainAxisIndex(axis);
3830:             }
3831:         }
3832:         return result;
3833:     }
3834: 
3835:     /**
3836:      * Returns the index of the given range axis.
3837:      *
3838:      * @param axis  the axis.
3839:      *
3840:      * @return The axis index.
3841:      * 
3842:      * @see #getDomainAxisIndex(ValueAxis)
3843:      */
3844:     public int getRangeAxisIndex(ValueAxis axis) {
3845:         int result = this.rangeAxes.indexOf(axis);
3846:         if (result < 0) {
3847:             // try the parent plot
3848:             Plot parent = getParent();
3849:             if (parent instanceof XYPlot) {
3850:                 XYPlot p = (XYPlot) parent;
3851:                 result = p.getRangeAxisIndex(axis);
3852:             }
3853:         }
3854:         return result;
3855:     }
3856: 
3857:     /**
3858:      * Returns the range for the specified axis.
3859:      *
3860:      * @param axis  the axis.
3861:      *
3862:      * @return The range.
3863:      */
3864:     public Range getDataRange(ValueAxis axis) {
3865: 
3866:         Range result = null;
3867:         List mappedDatasets = new ArrayList();
3868:         boolean isDomainAxis = true;
3869: 
3870:         // is it a domain axis?
3871:         int domainIndex = getDomainAxisIndex(axis);
3872:         if (domainIndex >= 0) {
3873:             isDomainAxis = true;
3874:             mappedDatasets.addAll(getDatasetsMappedToDomainAxis(
3875:                     new Integer(domainIndex)));
3876:         }
3877: 
3878:         // or is it a range axis?
3879:         int rangeIndex = getRangeAxisIndex(axis);
3880:         if (rangeIndex >= 0) {
3881:             isDomainAxis = false;
3882:             mappedDatasets.addAll(getDatasetsMappedToRangeAxis(
3883:                     new Integer(rangeIndex)));
3884:         }
3885: 
3886:         // iterate through the datasets that map to the axis and get the union
3887:         // of the ranges.
3888:         Iterator iterator = mappedDatasets.iterator();
3889:         while (iterator.hasNext()) {
3890:             XYDataset d = (XYDataset) iterator.next();
3891:             if (d != null) {
3892:                 XYItemRenderer r = getRendererForDataset(d);
3893:                 if (isDomainAxis) {
3894:                     if (r != null) {
3895:                         result = Range.combine(result, r.findDomainBounds(d));
3896:                     }
3897:                     else {
3898:                         result = Range.combine(result, 
3899:                                 DatasetUtilities.findDomainBounds(d));
3900:                     }
3901:                 }
3902:                 else {
3903:                     if (r != null) {
3904:                         result = Range.combine(result, r.findRangeBounds(d));
3905:                     }
3906:                     else {
3907:                         result = Range.combine(result, 
3908:                                 DatasetUtilities.findRangeBounds(d));
3909:                     }
3910:                 }
3911:             }
3912:         }
3913:         return result;
3914: 
3915:     }
3916: 
3917:     /**
3918:      * Receives notification of a change to the plot's dataset.
3919:      * <P>
3920:      * The axis ranges are updated if necessary.
3921:      *
3922:      * @param event  information about the event (not used here).
3923:      */
3924:     public void datasetChanged(DatasetChangeEvent event) {
3925:         configureDomainAxes();
3926:         configureRangeAxes();
3927:         if (getParent() != null) {
3928:             getParent().datasetChanged(event);
3929:         }
3930:         else {
3931:             PlotChangeEvent e = new PlotChangeEvent(this);
3932:             e.setType(ChartChangeEventType.DATASET_UPDATED);
3933:             notifyListeners(e);
3934:         }
3935:     }
3936: 
3937:     /**
3938:      * Receives notification of a renderer change event.
3939:      *
3940:      * @param event  the event.
3941:      */
3942:     public void rendererChanged(RendererChangeEvent event) {
3943:         notifyListeners(new PlotChangeEvent(this));
3944:     }
3945: 
3946:     /**
3947:      * Returns a flag indicating whether or not the domain crosshair is visible.
3948:      *
3949:      * @return The flag.
3950:      * 
3951:      * @see #setDomainCrosshairVisible(boolean)
3952:      */
3953:     public boolean isDomainCrosshairVisible() {
3954:         return this.domainCrosshairVisible;
3955:     }
3956: 
3957:     /**
3958:      * Sets the flag indicating whether or not the domain crosshair is visible 
3959:      * and, if the flag changes, sends a {@link PlotChangeEvent} to all 
3960:      * registered listeners.
3961:      *
3962:      * @param flag  the new value of the flag.
3963:      * 
3964:      * @see #isDomainCrosshairVisible()
3965:      */
3966:     public void setDomainCrosshairVisible(boolean flag) {
3967:         if (this.domainCrosshairVisible != flag) {
3968:             this.domainCrosshairVisible = flag;
3969:             notifyListeners(new PlotChangeEvent(this));
3970:         }
3971:     }
3972: 
3973:     /**
3974:      * Returns a flag indicating whether or not the crosshair should "lock-on"
3975:      * to actual data values.
3976:      *
3977:      * @return The flag.
3978:      * 
3979:      * @see #setDomainCrosshairLockedOnData(boolean)
3980:      */
3981:     public boolean isDomainCrosshairLockedOnData() {
3982:         return this.domainCrosshairLockedOnData;
3983:     }
3984: 
3985:     /**
3986:      * Sets the flag indicating whether or not the domain crosshair should
3987:      * "lock-on" to actual data values.  If the flag value changes, this
3988:      * method sends a {@link PlotChangeEvent} to all registered listeners.
3989:      *
3990:      * @param flag  the flag.
3991:      * 
3992:      * @see #isDomainCrosshairLockedOnData()
3993:      */
3994:     public void setDomainCrosshairLockedOnData(boolean flag) {
3995:         if (this.domainCrosshairLockedOnData != flag) {
3996:             this.domainCrosshairLockedOnData = flag;
3997:             notifyListeners(new PlotChangeEvent(this));
3998:         }
3999:     }
4000: 
4001:     /**
4002:      * Returns the domain crosshair value.
4003:      *
4004:      * @return The value.
4005:      * 
4006:      * @see #setDomainCrosshairValue(double)
4007:      */
4008:     public double getDomainCrosshairValue() {
4009:         return this.domainCrosshairValue;
4010:     }
4011: 
4012:     /**
4013:      * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to
4014:      * all registered listeners (provided that the domain crosshair is visible).
4015:      *
4016:      * @param value  the value.
4017:      * 
4018:      * @see #getDomainCrosshairValue()
4019:      */
4020:     public void setDomainCrosshairValue(double value) {
4021:         setDomainCrosshairValue(value, true);
4022:     }
4023: 
4024:     /**
4025:      * Sets the domain crosshair value and, if requested, sends a
4026:      * {@link PlotChangeEvent} to all registered listeners (provided that the
4027:      * domain crosshair is visible).
4028:      *
4029:      * @param value  the new value.
4030:      * @param notify  notify listeners?
4031:      * 
4032:      * @see #getDomainCrosshairValue()
4033:      */
4034:     public void setDomainCrosshairValue(double value, boolean notify) {
4035:         this.domainCrosshairValue = value;
4036:         if (isDomainCrosshairVisible() && notify) {
4037:             notifyListeners(new PlotChangeEvent(this));
4038:         }
4039:     }
4040: 
4041:     /**
4042:      * Returns the {@link Stroke} used to draw the crosshair (if visible).
4043:      *
4044:      * @return The crosshair stroke (never <code>null</code>).
4045:      * 
4046:      * @see #setDomainCrosshairStroke(Stroke)
4047:      * @see #isDomainCrosshairVisible()
4048:      * @see #getDomainCrosshairPaint()
4049:      */
4050:     public Stroke getDomainCrosshairStroke() {
4051:         return this.domainCrosshairStroke;
4052:     }
4053: 
4054:     /**
4055:      * Sets the Stroke used to draw the crosshairs (if visible) and notifies
4056:      * registered listeners that the axis has been modified.
4057:      *
4058:      * @param stroke  the new crosshair stroke (<code>null</code> not 
4059:      *     permitted).
4060:      *     
4061:      * @see #getDomainCrosshairStroke()
4062:      */
4063:     public void setDomainCrosshairStroke(Stroke stroke) {
4064:         if (stroke == null) { 
4065:             throw new IllegalArgumentException("Null 'stroke' argument.");
4066:         }
4067:         this.domainCrosshairStroke = stroke;
4068:         notifyListeners(new PlotChangeEvent(this));
4069:     }
4070: 
4071:     /**
4072:      * Returns the domain crosshair paint.
4073:      *
4074:      * @return The crosshair paint (never <code>null</code>).
4075:      * 
4076:      * @see #setDomainCrosshairPaint(Paint)
4077:      * @see #isDomainCrosshairVisible()
4078:      * @see #getDomainCrosshairStroke()
4079:      */
4080:     public Paint getDomainCrosshairPaint() {
4081:         return this.domainCrosshairPaint;
4082:     }
4083: 
4084:     /**
4085:      * Sets the paint used to draw the crosshairs (if visible) and sends a 
4086:      * {@link PlotChangeEvent} to all registered listeners.
4087:      *
4088:      * @param paint the new crosshair paint (<code>null</code> not permitted).
4089:      * 
4090:      * @see #getDomainCrosshairPaint()
4091:      */
4092:     public void setDomainCrosshairPaint(Paint paint) {
4093:         if (paint == null) {
4094:             throw new IllegalArgumentException("Null 'paint' argument.");
4095:         }
4096:         this.domainCrosshairPaint = paint;
4097:         notifyListeners(new PlotChangeEvent(this));
4098:     }
4099: 
4100:     /**
4101:      * Returns a flag indicating whether or not the range crosshair is visible.
4102:      *
4103:      * @return The flag.
4104:      * 
4105:      * @see #setRangeCrosshairVisible(boolean)
4106:      * @see #isDomainCrosshairVisible()
4107:      */
4108:     public boolean isRangeCrosshairVisible() {
4109:         return this.rangeCrosshairVisible;
4110:     }
4111: 
4112:     /**
4113:      * Sets the flag indicating whether or not the range crosshair is visible.
4114:      * If the flag value changes, this method sends a {@link PlotChangeEvent}
4115:      * to all registered listeners.
4116:      *
4117:      * @param flag  the new value of the flag.
4118:      * 
4119:      * @see #isRangeCrosshairVisible()
4120:      */
4121:     public void setRangeCrosshairVisible(boolean flag) {
4122:         if (this.rangeCrosshairVisible != flag) {
4123:             this.rangeCrosshairVisible = flag;
4124:             notifyListeners(new PlotChangeEvent(this));
4125:         }
4126:     }
4127: 
4128:     /**
4129:      * Returns a flag indicating whether or not the crosshair should "lock-on"
4130:      * to actual data values.
4131:      *
4132:      * @return The flag.
4133:      * 
4134:      * @see #setRangeCrosshairLockedOnData(boolean)
4135:      */
4136:     public boolean isRangeCrosshairLockedOnData() {
4137:         return this.rangeCrosshairLockedOnData;
4138:     }
4139: 
4140:     /**
4141:      * Sets the flag indicating whether or not the range crosshair should
4142:      * "lock-on" to actual data values.  If the flag value changes, this method
4143:      * sends a {@link PlotChangeEvent} to all registered listeners.
4144:      *
4145:      * @param flag  the flag.
4146:      * 
4147:      * @see #isRangeCrosshairLockedOnData()
4148:      */
4149:     public void setRangeCrosshairLockedOnData(boolean flag) {
4150:         if (this.rangeCrosshairLockedOnData != flag) {
4151:             this.rangeCrosshairLockedOnData = flag;
4152:             notifyListeners(new PlotChangeEvent(this));
4153:         }
4154:     }
4155: 
4156:     /**
4157:      * Returns the range crosshair value.
4158:      *
4159:      * @return The value.
4160:      * 
4161:      * @see #setRangeCrosshairValue(double)
4162:      */
4163:     public double getRangeCrosshairValue() {
4164:         return this.rangeCrosshairValue;
4165:     }
4166: 
4167:     /**
4168:      * Sets the range crosshair value.
4169:      * <P>
4170:      * Registered listeners are notified that the plot has been modified, but
4171:      * only if the crosshair is visible.
4172:      *
4173:      * @param value  the new value.
4174:      * 
4175:      * @see #getRangeCrosshairValue()
4176:      */
4177:     public void setRangeCrosshairValue(double value) {
4178:         setRangeCrosshairValue(value, true);
4179:     }
4180: 
4181:     /**
4182:      * Sets the range crosshair value and sends a {@link PlotChangeEvent} to
4183:      * all registered listeners, but only if the crosshair is visible.
4184:      *
4185:      * @param value  the new value.
4186:      * @param notify  a flag that controls whether or not listeners are
4187:      *                notified.
4188:      *                
4189:      * @see #getRangeCrosshairValue()
4190:      */
4191:     public void setRangeCrosshairValue(double value, boolean notify) {
4192:         this.rangeCrosshairValue = value;
4193:         if (isRangeCrosshairVisible() && notify) {
4194:             notifyListeners(new PlotChangeEvent(this));
4195:         }
4196:     }
4197: 
4198:     /**
4199:      * Returns the stroke used to draw the crosshair (if visible).
4200:      *
4201:      * @return The crosshair stroke (never <code>null</code>).
4202:      * 
4203:      * @see #setRangeCrosshairStroke(Stroke)
4204:      * @see #isRangeCrosshairVisible()
4205:      * @see #getRangeCrosshairPaint()
4206:      */
4207:     public Stroke getRangeCrosshairStroke() {
4208:         return this.rangeCrosshairStroke;
4209:     }
4210: 
4211:     /**
4212:      * Sets the stroke used to draw the crosshairs (if visible) and sends a 
4213:      * {@link PlotChangeEvent} to all registered listeners.
4214:      *
4215:      * @param stroke  the new crosshair stroke (<code>null</code> not 
4216:      *         permitted).
4217:      * 
4218:      * @see #getRangeCrosshairStroke()
4219:      */
4220:     public void setRangeCrosshairStroke(Stroke stroke) {
4221:         if (stroke == null) {
4222:             throw new IllegalArgumentException("Null 'stroke' argument.");
4223:         }
4224:         this.rangeCrosshairStroke = stroke;
4225:         notifyListeners(new PlotChangeEvent(this));
4226:     }
4227: 
4228:     /**
4229:      * Returns the range crosshair paint.
4230:      *
4231:      * @return The crosshair paint (never <code>null</code>).
4232:      * 
4233:      * @see #setRangeCrosshairPaint(Paint)
4234:      * @see #isRangeCrosshairVisible()
4235:      * @see #getRangeCrosshairStroke()
4236:      */
4237:     public Paint getRangeCrosshairPaint() {
4238:         return this.rangeCrosshairPaint;
4239:     }
4240: 
4241:     /**
4242:      * Sets the paint used to color the crosshairs (if visible) and sends a 
4243:      * {@link PlotChangeEvent} to all registered listeners.
4244:      *
4245:      * @param paint the new crosshair paint (<code>null</code> not permitted).
4246:      * 
4247:      * @see #getRangeCrosshairPaint()
4248:      */
4249:     public void setRangeCrosshairPaint(Paint paint) {
4250:         if (paint == null) {
4251:             throw new IllegalArgumentException("Null 'paint' argument.");
4252:         }
4253:         this.rangeCrosshairPaint = paint;
4254:         notifyListeners(new PlotChangeEvent(this));
4255:     }
4256: 
4257:     /**
4258:      * Returns the fixed domain axis space.
4259:      *
4260:      * @return The fixed domain axis space (possibly <code>null</code>).
4261:      * 
4262:      * @see #setFixedDomainAxisSpace(AxisSpace)
4263:      */
4264:     public AxisSpace getFixedDomainAxisSpace() {
4265:         return this.fixedDomainAxisSpace;
4266:     }
4267: 
4268:     /**
4269:      * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to
4270:      * all registered listeners.
4271:      *
4272:      * @param space  the space (<code>null</code> permitted).
4273:      * 
4274:      * @see #getFixedDomainAxisSpace()
4275:      */
4276:     public void setFixedDomainAxisSpace(AxisSpace space) {
4277:         setFixedDomainAxisSpace(space, true);
4278:     }
4279: 
4280:     /**
4281:      * Sets the fixed domain axis space and, if requested, sends a 
4282:      * {@link PlotChangeEvent} to all registered listeners.
4283:      *
4284:      * @param space  the space (<code>null</code> permitted).
4285:      * @param notify  notify listeners?
4286:      * 
4287:      * @see #getFixedDomainAxisSpace()
4288:      * 
4289:      * @since 1.0.9
4290:      */
4291:     public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) {
4292:         this.fixedDomainAxisSpace = space;
4293:         if (notify) {
4294:             notifyListeners(new PlotChangeEvent(this));
4295:         }
4296:     }
4297: 
4298:     /**
4299:      * Returns the fixed range axis space.
4300:      *
4301:      * @return The fixed range axis space (possibly <code>null</code>).
4302:      * 
4303:      * @see #setFixedRangeAxisSpace(AxisSpace)
4304:      */
4305:     public AxisSpace getFixedRangeAxisSpace() {
4306:         return this.fixedRangeAxisSpace;
4307:     }
4308: 
4309:     /**
4310:      * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to
4311:      * all registered listeners.
4312:      *
4313:      * @param space  the space (<code>null</code> permitted).
4314:      * 
4315:      * @see #getFixedRangeAxisSpace()
4316:      */
4317:     public void setFixedRangeAxisSpace(AxisSpace space) {
4318:         setFixedRangeAxisSpace(space, true);
4319:     }
4320: 
4321:     /**
4322:      * Sets the fixed range axis space and, if requested, sends a 
4323:      * {@link PlotChangeEvent} to all registered listeners.
4324:      *
4325:      * @param space  the space (<code>null</code> permitted).
4326:      * @param notify  notify listeners?
4327:      * 
4328:      * @see #getFixedRangeAxisSpace()
4329:      * 
4330:      * @since 1.0.9
4331:      */
4332:     public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) {
4333:         this.fixedRangeAxisSpace = space;
4334:         if (notify) {
4335:             notifyListeners(new PlotChangeEvent(this));
4336:         }
4337:     }
4338: 
4339:     /**
4340:      * Multiplies the range on the domain axis/axes by the specified factor.
4341:      *
4342:      * @param factor  the zoom factor.
4343:      * @param info  the plot rendering info.
4344:      * @param source  the source point (in Java2D space).
4345:      * 
4346:      * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D)
4347:      */
4348:     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
4349:                                Point2D source) {
4350:         // delegate to other method
4351:         zoomDomainAxes(factor, info, source, false);
4352:     }
4353: 
4354:     /**
4355:      * Multiplies the range on the domain axis/axes by the specified factor.
4356:      *
4357:      * @param factor  the zoom factor.
4358:      * @param info  the plot rendering info.
4359:      * @param source  the source point (in Java2D space).
4360:      * @param useAnchor  use source point as zoom anchor?
4361:      * 
4362:      * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean)
4363:      * 
4364:      * @since 1.0.7
4365:      */
4366:     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
4367:                                Point2D source, boolean useAnchor) {
4368:                 
4369:         // perform the zoom on each domain axis
4370:         for (int i = 0; i < this.domainAxes.size(); i++) {
4371:             ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
4372:             if (domainAxis != null) {
4373:                 if (useAnchor) {
4374:                     // get the relevant source coordinate given the plot 
4375:                     // orientation
4376:                     double sourceX = source.getX();
4377:                     if (this.orientation == PlotOrientation.HORIZONTAL) {
4378:                         sourceX = source.getY();
4379:                     }
4380:                     double anchorX = domainAxis.java2DToValue(sourceX, 
4381:                             info.getDataArea(), getDomainAxisEdge());
4382:                     domainAxis.resizeRange(factor, anchorX);
4383:                 }
4384:                 else {
4385:                     domainAxis.resizeRange(factor);
4386:                 }
4387:             }
4388:         }
4389:     }
4390: 
4391:     /**
4392:      * Zooms in on the domain axis/axes.  The new lower and upper bounds are
4393:      * specified as percentages of the current axis range, where 0 percent is
4394:      * the current lower bound and 100 percent is the current upper bound.
4395:      *
4396:      * @param lowerPercent  a percentage that determines the new lower bound
4397:      *                      for the axis (e.g. 0.20 is twenty percent).
4398:      * @param upperPercent  a percentage that determines the new upper bound
4399:      *                      for the axis (e.g. 0.80 is eighty percent).
4400:      * @param info  the plot rendering info.
4401:      * @param source  the source point (ignored).
4402:      * 
4403:      * @see #zoomRangeAxes(double, double, PlotRenderingInfo, Point2D)
4404:      */
4405:     public void zoomDomainAxes(double lowerPercent, double upperPercent,
4406:                                PlotRenderingInfo info, Point2D source) {
4407:         for (int i = 0; i < this.domainAxes.size(); i++) {
4408:             ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
4409:             if (domainAxis != null) {
4410:                 domainAxis.zoomRange(lowerPercent, upperPercent);
4411:             }
4412:         }
4413:     }
4414: 
4415:     /**
4416:      * Multiplies the range on the range axis/axes by the specified factor.
4417:      *
4418:      * @param factor  the zoom factor.
4419:      * @param info  the plot rendering info.
4420:      * @param source  the source point.
4421:      * 
4422:      * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean)
4423:      */
4424:     public void zoomRangeAxes(double factor, PlotRenderingInfo info,
4425:                               Point2D source) {
4426:         // delegate to other method
4427:         zoomRangeAxes(factor, info, source, false);    
4428:     }
4429:     
4430:     /**
4431:      * Multiplies the range on the range axis/axes by the specified factor.
4432:      *
4433:      * @param factor  the zoom factor.
4434:      * @param info  the plot rendering info.
4435:      * @param source  the source point.
4436:      * @param useAnchor  a flag that controls whether or not the source point
4437:      *         is used for the zoom anchor.
4438:      * 
4439:      * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean)
4440:      * 
4441:      * @since 1.0.7
4442:      */
4443:     public void zoomRangeAxes(double factor, PlotRenderingInfo info,
4444:                               Point2D source, boolean useAnchor) {
4445:                 
4446:         // perform the zoom on each range axis
4447:         for (int i = 0; i < this.rangeAxes.size(); i++) {
4448:             ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
4449:             if (rangeAxis != null) {
4450:                 if (useAnchor) {
4451:                     // get the relevant source coordinate given the plot 
4452:                     // orientation
4453:                     double sourceY = source.getY();
4454:                     if (this.orientation == PlotOrientation.HORIZONTAL) {
4455:                         sourceY = source.getX();
4456:                     }
4457:                     double anchorY = rangeAxis.java2DToValue(sourceY, 
4458:                             info.getDataArea(), getRangeAxisEdge());
4459:                     rangeAxis.resizeRange(factor, anchorY);
4460:                 }
4461:                 else {
4462:                     rangeAxis.resizeRange(factor);
4463:                 }
4464:             }
4465:         }
4466:     }
4467: 
4468:     /**
4469:      * Zooms in on the range axes.
4470:      *
4471:      * @param lowerPercent  the lower bound.
4472:      * @param upperPercent  the upper bound.
4473:      * @param info  the plot rendering info.
4474:      * @param source  the source point.
4475:      * 
4476:      * @see #zoomDomainAxes(double, double, PlotRenderingInfo, Point2D)
4477:      */
4478:     public void zoomRangeAxes(double lowerPercent, double upperPercent,
4479:                               PlotRenderingInfo info, Point2D source) {
4480:         for (int i = 0; i < this.rangeAxes.size(); i++) {
4481:             ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
4482:             if (rangeAxis != null) {
4483:                 rangeAxis.zoomRange(lowerPercent, upperPercent);
4484:             }
4485:         }
4486:     }
4487: 
4488:     /**
4489:      * Returns <code>true</code>, indicating that the domain axis/axes for this
4490:      * plot are zoomable.
4491:      *
4492:      * @return A boolean.
4493:      * 
4494:      * @see #isRangeZoomable()
4495:      */
4496:     public boolean isDomainZoomable() {
4497:         return true;
4498:     }
4499: 
4500:     /**
4501:      * Returns <code>true</code>, indicating that the range axis/axes for this
4502:      * plot are zoomable.
4503:      *
4504:      * @return A boolean.
4505:      * 
4506:      * @see #isDomainZoomable()
4507:      */
4508:     public boolean isRangeZoomable() {
4509:         return true;
4510:     }
4511: 
4512:     /**
4513:      * Returns the number of series in the primary dataset for this plot.  If
4514:      * the dataset is <code>null</code>, the method returns 0.
4515:      *
4516:      * @return The series count.
4517:      */
4518:     public int getSeriesCount() {
4519:         int result = 0;
4520:         XYDataset dataset = getDataset();
4521:         if (dataset != null) {
4522:             result = dataset.getSeriesCount();
4523:         }
4524:         return result;
4525:     }
4526: 
4527:     /**
4528:      * Returns the fixed legend items, if any.
4529:      *
4530:      * @return The legend items (possibly <code>null</code>).
4531:      * 
4532:      * @see #setFixedLegendItems(LegendItemCollection)
4533:      */
4534:     public LegendItemCollection getFixedLegendItems() {
4535:         return this.fixedLegendItems;
4536:     }
4537: 
4538:     /**
4539:      * Sets the fixed legend items for the plot.  Leave this set to
4540:      * <code>null</code> if you prefer the legend items to be created
4541:      * automatically.
4542:      *
4543:      * @param items  the legend items (<code>null</code> permitted).
4544:      * 
4545:      * @see #getFixedLegendItems()
4546:      */
4547:     public void setFixedLegendItems(LegendItemCollection items) {
4548:         this.fixedLegendItems = items;
4549:         notifyListeners(new PlotChangeEvent(this));
4550:     }
4551: 
4552:     /**
4553:      * Returns the legend items for the plot.  Each legend item is generated by
4554:      * the plot's renderer, since the renderer is responsible for the visual
4555:      * representation of the data.
4556:      *
4557:      * @return The legend items.
4558:      */
4559:     public LegendItemCollection getLegendItems() {
4560:         if (this.fixedLegendItems != null) {
4561:             return this.fixedLegendItems;
4562:         }
4563:         LegendItemCollection result = new LegendItemCollection();
4564:         int count = this.datasets.size();
4565:         for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
4566:             XYDataset dataset = getDataset(datasetIndex);
4567:             if (dataset != null) {
4568:                 XYItemRenderer renderer = getRenderer(datasetIndex);
4569:                 if (renderer == null) {
4570:                     renderer = getRenderer(0);
4571:                 }
4572:                 if (renderer != null) {
4573:                     int seriesCount = dataset.getSeriesCount();
4574:                     for (int i = 0; i < seriesCount; i++) {
4575:                         if (renderer.isSeriesVisible(i)
4576:                                 && renderer.isSeriesVisibleInLegend(i)) {
4577:                             LegendItem item = renderer.getLegendItem(
4578:                                     datasetIndex, i);
4579:                             if (item != null) {
4580:                                 result.add(item);
4581:                             }
4582:                         }
4583:                     }
4584:                 }
4585:             }
4586:         }
4587:         return result;
4588:     }
4589: 
4590:     /**
4591:      * Tests this plot for equality with another object.
4592:      *
4593:      * @param obj  the object (<code>null</code> permitted).
4594:      *
4595:      * @return <code>true</code> or <code>false</code>.
4596:      */
4597:     public boolean equals(Object obj) {
4598: 
4599:         if (obj == this) {
4600:             return true;
4601:         }
4602:         if (!(obj instanceof XYPlot)) {
4603:             return false;
4604:         }
4605: 
4606:         XYPlot that = (XYPlot) obj;
4607:         if (this.weight != that.weight) {
4608:             return false;
4609:         }
4610:         if (this.orientation != that.orientation) {
4611:             return false;
4612:         }
4613:         if (!this.domainAxes.equals(that.domainAxes)) {
4614:             return false;
4615:         }
4616:         if (!this.domainAxisLocations.equals(that.domainAxisLocations)) {
4617:             return false;
4618:         }
4619:         if (this.rangeCrosshairLockedOnData
4620:                 != that.rangeCrosshairLockedOnData) {
4621:             return false;
4622:         }
4623:         if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
4624:             return false;
4625:         }
4626:         if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) {
4627:             return false;
4628:         }
4629:         if (this.domainZeroBaselineVisible != that.domainZeroBaselineVisible) {
4630:             return false;
4631:         }
4632:         if (this.rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) {
4633:             return false;
4634:         }
4635:         if (this.domainCrosshairVisible != that.domainCrosshairVisible) {
4636:             return false;
4637:         }
4638:         if (this.domainCrosshairValue != that.domainCrosshairValue) {
4639:             return false;
4640:         }
4641:         if (this.domainCrosshairLockedOnData
4642:                 != that.domainCrosshairLockedOnData) {
4643:             return false;
4644:         }
4645:         if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) {
4646:             return false;
4647:         }
4648:         if (this.rangeCrosshairValue != that.rangeCrosshairValue) {
4649:             return false;
4650:         }
4651:         if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) {
4652:             return false;
4653:         }
4654:         if (!ObjectUtilities.equal(this.renderers, that.renderers)) {
4655:             return false;
4656:         }
4657:         if (!ObjectUtilities.equal(this.rangeAxes, that.rangeAxes)) {
4658:             return false;
4659:         }
4660:         if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) {
4661:             return false;
4662:         }
4663:         if (!ObjectUtilities.equal(this.datasetToDomainAxisMap, 
4664:                 that.datasetToDomainAxisMap)) {
4665:             return false;
4666:         }
4667:         if (!ObjectUtilities.equal(this.datasetToRangeAxisMap, 
4668:                 that.datasetToRangeAxisMap)) {
4669:             return false;
4670:         }
4671:         if (!ObjectUtilities.equal(this.domainGridlineStroke, 
4672:                 that.domainGridlineStroke)) {
4673:             return false;
4674:         }
4675:         if (!PaintUtilities.equal(this.domainGridlinePaint, 
4676:                 that.domainGridlinePaint)) {
4677:             return false;
4678:         }
4679:         if (!ObjectUtilities.equal(this.rangeGridlineStroke, 
4680:                 that.rangeGridlineStroke)) {
4681:             return false;
4682:         }
4683:         if (!PaintUtilities.equal(this.rangeGridlinePaint, 
4684:                 that.rangeGridlinePaint)) {
4685:             return false;
4686:         }
4687:         if (!PaintUtilities.equal(this.domainZeroBaselinePaint, 
4688:                 that.domainZeroBaselinePaint)) {
4689:             return false;
4690:         }
4691:         if (!ObjectUtilities.equal(this.domainZeroBaselineStroke, 
4692:                 that.domainZeroBaselineStroke)) {
4693:             return false;
4694:         }
4695:         if (!PaintUtilities.equal(this.rangeZeroBaselinePaint, 
4696:                 that.rangeZeroBaselinePaint)) {
4697:             return false;
4698:         }
4699:         if (!ObjectUtilities.equal(this.rangeZeroBaselineStroke, 
4700:                 that.rangeZeroBaselineStroke)) {
4701:             return false;
4702:         }
4703:         if (!ObjectUtilities.equal(this.domainCrosshairStroke, 
4704:                 that.domainCrosshairStroke)) {
4705:             return false;
4706:         }
4707:         if (!PaintUtilities.equal(this.domainCrosshairPaint, 
4708:                 that.domainCrosshairPaint)) {
4709:             return false;
4710:         }
4711:         if (!ObjectUtilities.equal(this.rangeCrosshairStroke, 
4712:                 that.rangeCrosshairStroke)) {
4713:             return false;
4714:         }
4715:         if (!PaintUtilities.equal(this.rangeCrosshairPaint, 
4716:                 that.rangeCrosshairPaint)) {
4717:             return false;
4718:         }
4719:         if (!ObjectUtilities.equal(this.foregroundDomainMarkers, 
4720:                 that.foregroundDomainMarkers)) {
4721:             return false;
4722:         }
4723:         if (!ObjectUtilities.equal(this.backgroundDomainMarkers, 
4724:                 that.backgroundDomainMarkers)) {
4725:             return false;
4726:         }
4727:         if (!ObjectUtilities.equal(this.foregroundRangeMarkers, 
4728:                 that.foregroundRangeMarkers)) {
4729:             return false;
4730:         }
4731:         if (!ObjectUtilities.equal(this.backgroundRangeMarkers, 
4732:                 that.backgroundRangeMarkers)) {
4733:             return false;
4734:         }
4735:         if (!ObjectUtilities.equal(this.foregroundDomainMarkers, 
4736:                 that.foregroundDomainMarkers)) {
4737:             return false;
4738:         }
4739:         if (!ObjectUtilities.equal(this.backgroundDomainMarkers, 
4740:                 that.backgroundDomainMarkers)) {
4741:             return false;
4742:         }
4743:         if (!ObjectUtilities.equal(this.foregroundRangeMarkers, 
4744:                 that.foregroundRangeMarkers)) {
4745:             return false;
4746:         }
4747:         if (!ObjectUtilities.equal(this.backgroundRangeMarkers, 
4748:                 that.backgroundRangeMarkers)) {
4749:             return false;
4750:         }
4751:         if (!ObjectUtilities.equal(this.annotations, that.annotations)) {
4752:             return false;
4753:         }
4754:         if (!PaintUtilities.equal(this.domainTickBandPaint, 
4755:                 that.domainTickBandPaint)) {
4756:             return false;
4757:         }
4758:         if (!PaintUtilities.equal(this.rangeTickBandPaint, 
4759:                 that.rangeTickBandPaint)) {
4760:             return false;
4761:         }
4762:         if (!this.quadrantOrigin.equals(that.quadrantOrigin)) {
4763:             return false;
4764:         }
4765:         for (int i = 0; i < 4; i++) {
4766:             if (!PaintUtilities.equal(this.quadrantPaint[i], 
4767:                     that.quadrantPaint[i])) {
4768:                 return false;
4769:             }
4770:         }
4771:         return super.equals(obj);
4772:     }
4773: 
4774:     /**
4775:      * Returns a clone of the plot.
4776:      *
4777:      * @return A clone.
4778:      *
4779:      * @throws CloneNotSupportedException  this can occur if some component of
4780:      *         the plot cannot be cloned.
4781:      */
4782:     public Object clone() throws CloneNotSupportedException {
4783: 
4784:         XYPlot clone = (XYPlot) super.clone();
4785:         clone.domainAxes = (ObjectList) ObjectUtilities.clone(this.domainAxes);
4786:         for (int i = 0; i < this.domainAxes.size(); i++) {
4787:             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
4788:             if (axis != null) {
4789:                 ValueAxis clonedAxis = (ValueAxis) axis.clone();
4790:                 clone.domainAxes.set(i, clonedAxis);
4791:                 clonedAxis.setPlot(clone);
4792:                 clonedAxis.addChangeListener(clone);
4793:             }
4794:         }
4795:         clone.domainAxisLocations = (ObjectList) 
4796:                 this.domainAxisLocations.clone();
4797: 
4798:         clone.rangeAxes = (ObjectList) ObjectUtilities.clone(this.rangeAxes);
4799:         for (int i = 0; i < this.rangeAxes.size(); i++) {
4800:             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
4801:             if (axis != null) {
4802:                 ValueAxis clonedAxis = (ValueAxis) axis.clone();
4803:                 clone.rangeAxes.set(i, clonedAxis);
4804:                 clonedAxis.setPlot(clone);
4805:                 clonedAxis.addChangeListener(clone);
4806:             }
4807:         }
4808:         clone.rangeAxisLocations = (ObjectList) ObjectUtilities.clone(
4809:                 this.rangeAxisLocations);
4810: 
4811:         // the datasets are not cloned, but listeners need to be added...
4812:         clone.datasets = (ObjectList) ObjectUtilities.clone(this.datasets);
4813:         for (int i = 0; i < clone.datasets.size(); ++i) {
4814:             XYDataset d = getDataset(i);
4815:             if (d != null) {
4816:                 d.addChangeListener(clone);
4817:             }
4818:         }
4819: 
4820:         clone.datasetToDomainAxisMap = new TreeMap();
4821:         clone.datasetToDomainAxisMap.putAll(this.datasetToDomainAxisMap);
4822:         clone.datasetToRangeAxisMap = new TreeMap();
4823:         clone.datasetToRangeAxisMap.putAll(this.datasetToRangeAxisMap);
4824: 
4825:         clone.renderers = (ObjectList) ObjectUtilities.clone(this.renderers);
4826:         for (int i = 0; i < this.renderers.size(); i++) {
4827:             XYItemRenderer renderer2 = (XYItemRenderer) this.renderers.get(i);
4828:             if (renderer2 instanceof PublicCloneable) {
4829:                 PublicCloneable pc = (PublicCloneable) renderer2;
4830:                 clone.renderers.set(i, pc.clone());
4831:             }
4832:         }
4833:         clone.foregroundDomainMarkers = (Map) ObjectUtilities.clone(
4834:                 this.foregroundDomainMarkers);
4835:         clone.backgroundDomainMarkers = (Map) ObjectUtilities.clone(
4836:                 this.backgroundDomainMarkers);
4837:         clone.foregroundRangeMarkers = (Map) ObjectUtilities.clone(
4838:                 this.foregroundRangeMarkers);
4839:         clone.backgroundRangeMarkers = (Map) ObjectUtilities.clone(
4840:                 this.backgroundRangeMarkers);
4841:         clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
4842:         if (this.fixedDomainAxisSpace != null) {
4843:             clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone(
4844:                     this.fixedDomainAxisSpace);
4845:         }
4846:         if (this.fixedRangeAxisSpace != null) {
4847:             clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone(
4848:                     this.fixedRangeAxisSpace);
4849:         }
4850: 
4851:         clone.quadrantOrigin = (Point2D) ObjectUtilities.clone(
4852:                 this.quadrantOrigin);
4853:         clone.quadrantPaint = (Paint[]) this.quadrantPaint.clone();
4854:         return clone;
4855: 
4856:     }
4857: 
4858:     /**
4859:      * Provides serialization support.
4860:      *
4861:      * @param stream  the output stream.
4862:      *
4863:      * @throws IOException  if there is an I/O error.
4864:      */
4865:     private void writeObject(ObjectOutputStream stream) throws IOException {
4866:         stream.defaultWriteObject();
4867:         SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
4868:         SerialUtilities.writePaint(this.domainGridlinePaint, stream);
4869:         SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
4870:         SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
4871:         SerialUtilities.writeStroke(this.rangeZeroBaselineStroke, stream);
4872:         SerialUtilities.writePaint(this.rangeZeroBaselinePaint, stream);
4873:         SerialUtilities.writeStroke(this.domainCrosshairStroke, stream);
4874:         SerialUtilities.writePaint(this.domainCrosshairPaint, stream);
4875:         SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream);
4876:         SerialUtilities.writePaint(this.rangeCrosshairPaint, stream);
4877:         SerialUtilities.writePaint(this.domainTickBandPaint, stream);
4878:         SerialUtilities.writePaint(this.rangeTickBandPaint, stream);
4879:         SerialUtilities.writePoint2D(this.quadrantOrigin, stream);
4880:         for (int i = 0; i < 4; i++) {
4881:             SerialUtilities.writePaint(this.quadrantPaint[i], stream);
4882:         }
4883:         SerialUtilities.writeStroke(this.domainZeroBaselineStroke, stream);
4884:         SerialUtilities.writePaint(this.domainZeroBaselinePaint, stream);
4885:     }
4886: 
4887:     /**
4888:      * Provides serialization support.
4889:      *
4890:      * @param stream  the input stream.
4891:      *
4892:      * @throws IOException  if there is an I/O error.
4893:      * @throws ClassNotFoundException  if there is a classpath problem.
4894:      */
4895:     private void readObject(ObjectInputStream stream)
4896:         throws IOException, ClassNotFoundException {
4897: 
4898:         stream.defaultReadObject();
4899:         this.domainGridlineStroke = SerialUtilities.readStroke(stream);
4900:         this.domainGridlinePaint = SerialUtilities.readPaint(stream);
4901:         this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
4902:         this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
4903:         this.rangeZeroBaselineStroke = SerialUtilities.readStroke(stream);
4904:         this.rangeZeroBaselinePaint = SerialUtilities.readPaint(stream);
4905:         this.domainCrosshairStroke = SerialUtilities.readStroke(stream);
4906:         this.domainCrosshairPaint = SerialUtilities.readPaint(stream);
4907:         this.rangeCrosshairStroke = SerialUtilities.readStroke(stream);
4908:         this.rangeCrosshairPaint = SerialUtilities.readPaint(stream);
4909:         this.domainTickBandPaint = SerialUtilities.readPaint(stream);
4910:         this.rangeTickBandPaint = SerialUtilities.readPaint(stream);
4911:         this.quadrantOrigin = SerialUtilities.readPoint2D(stream);
4912:         this.quadrantPaint = new Paint[4];
4913:         for (int i = 0; i < 4; i++) {
4914:             this.quadrantPaint[i] = SerialUtilities.readPaint(stream);
4915:         }
4916: 
4917:         this.domainZeroBaselineStroke = SerialUtilities.readStroke(stream);
4918:         this.domainZeroBaselinePaint = SerialUtilities.readPaint(stream);
4919: 
4920:         // register the plot as a listener with its axes, datasets, and 
4921:         // renderers...
4922:         int domainAxisCount = this.domainAxes.size();
4923:         for (int i = 0; i < domainAxisCount; i++) {
4924:             Axis axis = (Axis) this.domainAxes.get(i);
4925:             if (axis != null) {
4926:                 axis.setPlot(this);
4927:                 axis.addChangeListener(this);
4928:             }
4929:         }
4930:         int rangeAxisCount = this.rangeAxes.size();
4931:         for (int i = 0; i < rangeAxisCount; i++) {
4932:             Axis axis = (Axis) this.rangeAxes.get(i);
4933:             if (axis != null) {
4934:                 axis.setPlot(this);
4935:                 axis.addChangeListener(this);
4936:             }
4937:         }
4938:         int datasetCount = this.datasets.size();
4939:         for (int i = 0; i < datasetCount; i++) {
4940:             Dataset dataset = (Dataset) this.datasets.get(i);
4941:             if (dataset != null) {
4942:                 dataset.addChangeListener(this);
4943:             }
4944:         }
4945:         int rendererCount = this.renderers.size();
4946:         for (int i = 0; i < rendererCount; i++) {
4947:             XYItemRenderer renderer = (XYItemRenderer) this.renderers.get(i);
4948:             if (renderer != null) {
4949:                 renderer.addChangeListener(this);
4950:             }
4951:         }
4952:     
4953:     }
4954: 
4955: }