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