Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » plot » [javadoc | source]
    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