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    * CategoryPlot.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):   Jeremy Bowman;
   34    *                   Arnaud Lelievre;
   35    *                   Richard West, Advanced Micro Devices, Inc.;
   36    *
   37    * Changes
   38    * -------
   39    * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
   40    * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
   41    * 18-Sep-2001 : Updated header (DG);
   42    * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
   43    * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
   44    * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
   45    *               available space rather than a fixed number of units (DG);
   46    * 12-Dec-2001 : Changed constructors to protected (DG);
   47    * 13-Dec-2001 : Added tooltips (DG);
   48    * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
   49    *               some argument checking code.  Thanks to Taoufik Romdhane for
   50    *               suggesting this (DG);
   51    * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
   52    *               alpha-transparency for Plot and subclasses (DG);
   53    * 06-Mar-2002 : Updated import statements (DG);
   54    * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
   55    *               to use the CategoryItemRenderer interface (DG);
   56    * 22-Mar-2002 : Dropped the getCategories() method (DG);
   57    * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
   58    *               class (DG);
   59    * 29-Apr-2002 : New methods to support printing values at the end of bars,
   60    *               contributed by Jeremy Bowman (DG);
   61    * 11-May-2002 : New methods for label visibility and overlaid plot support,
   62    *               contributed by Jeremy Bowman (DG);
   63    * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
   64    *               renderer.  Moved constants into the CategoryPlotConstants
   65    *               interface.  Updated Javadoc comments (DG);
   66    * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
   67    *               lower bound on the range axis (if necessary), updated
   68    *               Javadocs (DG);
   69    * 25-Jun-2002 : Removed redundant imports (DG);
   70    * 20-Aug-2002 : Changed the constructor for Marker (DG);
   71    * 28-Aug-2002 : Added listener notification to setDomainAxis() and
   72    *               setRangeAxis() (DG);
   73    * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
   74    *               Checkstyle (DG);
   75    * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
   76    * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
   77    * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
   78    * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
   79    *               these were set in the axes) (DG);
   80    * 19-Nov-2002 : Added axis location parameters to constructor (DG);
   81    * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
   82    * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
   83    * 26-Mar-2003 : Implemented Serializable (DG);
   84    * 02-May-2003 : Moved render() method up from subclasses. Added secondary
   85    *               range markers. Added an attribute to control the dataset
   86    *               rendering order.  Added a drawAnnotations() method.  Changed
   87    *               the axis location from an int to an AxisLocation (DG);
   88    * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
   89    *               this class (DG);
   90    * 02-Jun-2003 : Removed check for range axis compatibility (DG);
   91    * 04-Jul-2003 : Added a domain gridline position attribute (DG);
   92    * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
   93    * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
   94    * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
   95    *               changes) (DG);
   96    * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
   97    *               790407 (initialise method) (DG);
   98    * 08-Sep-2003 : Added internationalization via use of properties
   99    *               resourceBundle (RFE 690236) (AL);
  100    * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used).  Changed
  101    *               ValueAxis API (DG);
  102    * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
  103    * 15-Sep-2003 : Fixed two bugs in serialization, implemented
  104    *               PublicCloneable (DG);
  105    * 23-Oct-2003 : Added event notification for changes to renderer (DG);
  106    * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
  107    * 03-Dec-2003 : Modified draw method to accept anchor (DG);
  108    * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
  109    * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
  110    *               stacked (DG);
  111    * 12-May-2004 : Added fixed legend items (DG);
  112    * 19-May-2004 : Added check for null legend item from renderer (DG);
  113    * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
  114    * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
  115    *               --> datasetsMappedToRangeAxis(), and ensured that returned
  116    *               list doesn't contain null datasets (DG);
  117    * 12-Nov-2004 : Implemented new Zoomable interface (DG);
  118    * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
  119    *               CategoryItemRenderer (DG);
  120    * 04-May-2005 : Fixed serialization of range markers (DG);
  121    * 05-May-2005 : Updated draw() method parameters (DG);
  122    * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
  123    *               RFE 1183100 (DG);
  124    * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
  125    *               axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
  126    * 02-Jun-2005 : Added support for domain markers (DG);
  127    * 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG);
  128    * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
  129    * 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to
  130    *               match XYPlot (see RFE 1220495) (DG);
  131    * ------------- JFREECHART 1.0.x ---------------------------------------------
  132    * 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the
  133    *               renderer might influence the axis range (DG);
  134    * 27-Jan-2006 : Added various null argument checks (DG);
  135    * 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing
  136    *               category labels, thanks to Adriaan Joubert (1277726) (DG);
  137    * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
  138    * 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and
  139    *               getCategoriesForAxis() methods (DG);
  140    * 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and
  141    *               setRowRenderingOrder() (DG);
  142    * 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data
  143    *               area) (DG);
  144    * 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument
  145    *               ignored) (DG);
  146    * 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and
  147    *               setRangeCrosshairStroke(), fixed clipping for
  148    *               annotations (DG);
  149    * 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG);
  150    * 10-Jul-2007 : Added getRangeAxisIndex(ValueAxis) method (DG);
  151    * 24-Sep-2007 : Implemented new zoom methods (DG);
  152    * 25-Oct-2007 : Added some argument checks (DG);
  153    * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
  154    *               and range markers (DG);
  155    * 14-Nov-2007 : Added missing event notifications (DG);
  156    * 25-Mar-2008 : Added new methods with optional notification - see patch
  157    *               1913751 (DG);
  158    * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and
  159    *               removeRangeMarker() (DG);
  160    * 23-Apr-2008 : Fixed equals() and clone() methods (DG);
  161    *
  162    *
  163    */
  164   
  165   package org.jfree.chart.plot;
  166   
  167   import java.awt.AlphaComposite;
  168   import java.awt.BasicStroke;
  169   import java.awt.Color;
  170   import java.awt.Composite;
  171   import java.awt.Font;
  172   import java.awt.Graphics2D;
  173   import java.awt.Paint;
  174   import java.awt.Shape;
  175   import java.awt.Stroke;
  176   import java.awt.geom.Line2D;
  177   import java.awt.geom.Point2D;
  178   import java.awt.geom.Rectangle2D;
  179   import java.io.IOException;
  180   import java.io.ObjectInputStream;
  181   import java.io.ObjectOutputStream;
  182   import java.io.Serializable;
  183   import java.util.ArrayList;
  184   import java.util.Collection;
  185   import java.util.Collections;
  186   import java.util.HashMap;
  187   import java.util.Iterator;
  188   import java.util.List;
  189   import java.util.Map;
  190   import java.util.ResourceBundle;
  191   import java.util.Set;
  192   
  193   import org.jfree.chart.LegendItem;
  194   import org.jfree.chart.LegendItemCollection;
  195   import org.jfree.chart.annotations.CategoryAnnotation;
  196   import org.jfree.chart.axis.Axis;
  197   import org.jfree.chart.axis.AxisCollection;
  198   import org.jfree.chart.axis.AxisLocation;
  199   import org.jfree.chart.axis.AxisSpace;
  200   import org.jfree.chart.axis.AxisState;
  201   import org.jfree.chart.axis.CategoryAnchor;
  202   import org.jfree.chart.axis.CategoryAxis;
  203   import org.jfree.chart.axis.ValueAxis;
  204   import org.jfree.chart.axis.ValueTick;
  205   import org.jfree.chart.event.ChartChangeEventType;
  206   import org.jfree.chart.event.PlotChangeEvent;
  207   import org.jfree.chart.event.RendererChangeEvent;
  208   import org.jfree.chart.event.RendererChangeListener;
  209   import org.jfree.chart.renderer.category.CategoryItemRenderer;
  210   import org.jfree.chart.renderer.category.CategoryItemRendererState;
  211   import org.jfree.data.Range;
  212   import org.jfree.data.category.CategoryDataset;
  213   import org.jfree.data.general.Dataset;
  214   import org.jfree.data.general.DatasetChangeEvent;
  215   import org.jfree.data.general.DatasetUtilities;
  216   import org.jfree.io.SerialUtilities;
  217   import org.jfree.ui.Layer;
  218   import org.jfree.ui.RectangleEdge;
  219   import org.jfree.ui.RectangleInsets;
  220   import org.jfree.util.ObjectList;
  221   import org.jfree.util.ObjectUtilities;
  222   import org.jfree.util.PaintUtilities;
  223   import org.jfree.util.PublicCloneable;
  224   import org.jfree.util.SortOrder;
  225   
  226   /**
  227    * A general plotting class that uses data from a {@link CategoryDataset} and
  228    * renders each data item using a {@link CategoryItemRenderer}.
  229    */
  230   public class CategoryPlot extends Plot implements ValueAxisPlot,
  231           Zoomable, RendererChangeListener, Cloneable, PublicCloneable,
  232           Serializable {
  233   
  234       /** For serialization. */
  235       private static final long serialVersionUID = -3537691700434728188L;
  236   
  237       /**
  238        * The default visibility of the grid lines plotted against the domain
  239        * axis.
  240        */
  241       public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
  242   
  243       /**
  244        * The default visibility of the grid lines plotted against the range
  245        * axis.
  246        */
  247       public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
  248   
  249       /** The default grid line stroke. */
  250       public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
  251               BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[]
  252               {2.0f, 2.0f}, 0.0f);
  253   
  254       /** The default grid line paint. */
  255       public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
  256   
  257       /** The default value label font. */
  258       public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif",
  259               Font.PLAIN, 10);
  260   
  261       /**
  262        * The default crosshair visibility.
  263        *
  264        * @since 1.0.5
  265        */
  266       public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
  267   
  268       /**
  269        * The default crosshair stroke.
  270        *
  271        * @since 1.0.5
  272        */
  273       public static final Stroke DEFAULT_CROSSHAIR_STROKE
  274               = DEFAULT_GRIDLINE_STROKE;
  275   
  276       /**
  277        * The default crosshair paint.
  278        *
  279        * @since 1.0.5
  280        */
  281       public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
  282   
  283       /** The resourceBundle for the localization. */
  284       protected static ResourceBundle localizationResources
  285               = ResourceBundle.getBundle(
  286               "org.jfree.chart.plot.LocalizationBundle");
  287   
  288       /** The plot orientation. */
  289       private PlotOrientation orientation;
  290   
  291       /** The offset between the data area and the axes. */
  292       private RectangleInsets axisOffset;
  293   
  294       /** Storage for the domain axes. */
  295       private ObjectList domainAxes;
  296   
  297       /** Storage for the domain axis locations. */
  298       private ObjectList domainAxisLocations;
  299   
  300       /**
  301        * A flag that controls whether or not the shared domain axis is drawn
  302        * (only relevant when the plot is being used as a subplot).
  303        */
  304       private boolean drawSharedDomainAxis;
  305   
  306       /** Storage for the range axes. */
  307       private ObjectList rangeAxes;
  308   
  309       /** Storage for the range axis locations. */
  310       private ObjectList rangeAxisLocations;
  311   
  312       /** Storage for the datasets. */
  313       private ObjectList datasets;
  314   
  315       /** Storage for keys that map datasets to domain axes. */
  316       private ObjectList datasetToDomainAxisMap;
  317   
  318       /** Storage for keys that map datasets to range axes. */
  319       private ObjectList datasetToRangeAxisMap;
  320   
  321       /** Storage for the renderers. */
  322       private ObjectList renderers;
  323   
  324       /** The dataset rendering order. */
  325       private DatasetRenderingOrder renderingOrder
  326               = DatasetRenderingOrder.REVERSE;
  327   
  328       /**
  329        * Controls the order in which the columns are traversed when rendering the
  330        * data items.
  331        */
  332       private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
  333   
  334       /**
  335        * Controls the order in which the rows are traversed when rendering the
  336        * data items.
  337        */
  338       private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
  339   
  340       /**
  341        * A flag that controls whether the grid-lines for the domain axis are
  342        * visible.
  343        */
  344       private boolean domainGridlinesVisible;
  345   
  346       /** The position of the domain gridlines relative to the category. */
  347       private CategoryAnchor domainGridlinePosition;
  348   
  349       /** The stroke used to draw the domain grid-lines. */
  350       private transient Stroke domainGridlineStroke;
  351   
  352       /** The paint used to draw the domain  grid-lines. */
  353       private transient Paint domainGridlinePaint;
  354   
  355       /**
  356        * A flag that controls whether the grid-lines for the range axis are
  357        * visible.
  358        */
  359       private boolean rangeGridlinesVisible;
  360   
  361       /** The stroke used to draw the range axis grid-lines. */
  362       private transient Stroke rangeGridlineStroke;
  363   
  364       /** The paint used to draw the range axis grid-lines. */
  365       private transient Paint rangeGridlinePaint;
  366   
  367       /** The anchor value. */
  368       private double anchorValue;
  369   
  370       /** A flag that controls whether or not a range crosshair is drawn. */
  371       private boolean rangeCrosshairVisible;
  372   
  373       /** The range crosshair value. */
  374       private double rangeCrosshairValue;
  375   
  376       /** The pen/brush used to draw the crosshair (if any). */
  377       private transient Stroke rangeCrosshairStroke;
  378   
  379       /** The color used to draw the crosshair (if any). */
  380       private transient Paint rangeCrosshairPaint;
  381   
  382       /**
  383        * A flag that controls whether or not the crosshair locks onto actual
  384        * data points.
  385        */
  386       private boolean rangeCrosshairLockedOnData = true;
  387   
  388       /** A map containing lists of markers for the domain axes. */
  389       private Map foregroundDomainMarkers;
  390   
  391       /** A map containing lists of markers for the domain axes. */
  392       private Map backgroundDomainMarkers;
  393   
  394       /** A map containing lists of markers for the range axes. */
  395       private Map foregroundRangeMarkers;
  396   
  397       /** A map containing lists of markers for the range axes. */
  398       private Map backgroundRangeMarkers;
  399   
  400       /**
  401        * A (possibly empty) list of annotations for the plot.  The list should
  402        * be initialised in the constructor and never allowed to be
  403        * <code>null</code>.
  404        */
  405       private List annotations;
  406   
  407       /**
  408        * The weight for the plot (only relevant when the plot is used as a subplot
  409        * within a combined plot).
  410        */
  411       private int weight;
  412   
  413       /** The fixed space for the domain axis. */
  414       private AxisSpace fixedDomainAxisSpace;
  415   
  416       /** The fixed space for the range axis. */
  417       private AxisSpace fixedRangeAxisSpace;
  418   
  419       /**
  420        * An optional collection of legend items that can be returned by the
  421        * getLegendItems() method.
  422        */
  423       private LegendItemCollection fixedLegendItems;
  424   
  425       /**
  426        * Default constructor.
  427        */
  428       public CategoryPlot() {
  429           this(null, null, null, null);
  430       }
  431   
  432       /**
  433        * Creates a new plot.
  434        *
  435        * @param dataset  the dataset (<code>null</code> permitted).
  436        * @param domainAxis  the domain axis (<code>null</code> permitted).
  437        * @param rangeAxis  the range axis (<code>null</code> permitted).
  438        * @param renderer  the item renderer (<code>null</code> permitted).
  439        *
  440        */
  441       public CategoryPlot(CategoryDataset dataset,
  442                           CategoryAxis domainAxis,
  443                           ValueAxis rangeAxis,
  444                           CategoryItemRenderer renderer) {
  445   
  446           super();
  447   
  448           this.orientation = PlotOrientation.VERTICAL;
  449   
  450           // allocate storage for dataset, axes and renderers
  451           this.domainAxes = new ObjectList();
  452           this.domainAxisLocations = new ObjectList();
  453           this.rangeAxes = new ObjectList();
  454           this.rangeAxisLocations = new ObjectList();
  455   
  456           this.datasetToDomainAxisMap = new ObjectList();
  457           this.datasetToRangeAxisMap = new ObjectList();
  458   
  459           this.renderers = new ObjectList();
  460   
  461           this.datasets = new ObjectList();
  462           this.datasets.set(0, dataset);
  463           if (dataset != null) {
  464               dataset.addChangeListener(this);
  465           }
  466   
  467           this.axisOffset = RectangleInsets.ZERO_INSETS;
  468   
  469           setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
  470           setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
  471   
  472           this.renderers.set(0, renderer);
  473           if (renderer != null) {
  474               renderer.setPlot(this);
  475               renderer.addChangeListener(this);
  476           }
  477   
  478           this.domainAxes.set(0, domainAxis);
  479           this.mapDatasetToDomainAxis(0, 0);
  480           if (domainAxis != null) {
  481               domainAxis.setPlot(this);
  482               domainAxis.addChangeListener(this);
  483           }
  484           this.drawSharedDomainAxis = false;
  485   
  486           this.rangeAxes.set(0, rangeAxis);
  487           this.mapDatasetToRangeAxis(0, 0);
  488           if (rangeAxis != null) {
  489               rangeAxis.setPlot(this);
  490               rangeAxis.addChangeListener(this);
  491           }
  492   
  493           configureDomainAxes();
  494           configureRangeAxes();
  495   
  496           this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
  497           this.domainGridlinePosition = CategoryAnchor.MIDDLE;
  498           this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
  499           this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
  500   
  501           this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
  502           this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
  503           this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
  504   
  505           this.foregroundDomainMarkers = new HashMap();
  506           this.backgroundDomainMarkers = new HashMap();
  507           this.foregroundRangeMarkers = new HashMap();
  508           this.backgroundRangeMarkers = new HashMap();
  509   
  510           Marker baseline = new ValueMarker(0.0, new Color(0.8f, 0.8f, 0.8f,
  511                   0.5f), new BasicStroke(1.0f), new Color(0.85f, 0.85f, 0.95f,
  512                   0.5f), new BasicStroke(1.0f), 0.6f);
  513           addRangeMarker(baseline, Layer.BACKGROUND);
  514   
  515           this.anchorValue = 0.0;
  516   
  517           this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE;
  518           this.rangeCrosshairValue = 0.0;
  519           this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
  520           this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
  521   
  522           this.annotations = new java.util.ArrayList();
  523   
  524       }
  525   
  526       /**
  527        * Returns a string describing the type of plot.
  528        *
  529        * @return The type.
  530        */
  531       public String getPlotType() {
  532           return localizationResources.getString("Category_Plot");
  533       }
  534   
  535       /**
  536        * Returns the orientation of the plot.
  537        *
  538        * @return The orientation of the plot (never <code>null</code>).
  539        *
  540        * @see #setOrientation(PlotOrientation)
  541        */
  542       public PlotOrientation getOrientation() {
  543           return this.orientation;
  544       }
  545   
  546       /**
  547        * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
  548        * all registered listeners.
  549        *
  550        * @param orientation  the orientation (<code>null</code> not permitted).
  551        *
  552        * @see #getOrientation()
  553        */
  554       public void setOrientation(PlotOrientation orientation) {
  555           if (orientation == null) {
  556               throw new IllegalArgumentException("Null 'orientation' argument.");
  557           }
  558           this.orientation = orientation;
  559           fireChangeEvent();
  560       }
  561   
  562       /**
  563        * Returns the axis offset.
  564        *
  565        * @return The axis offset (never <code>null</code>).
  566        *
  567        * @see #setAxisOffset(RectangleInsets)
  568        */
  569       public RectangleInsets getAxisOffset() {
  570           return this.axisOffset;
  571       }
  572   
  573       /**
  574        * Sets the axis offsets (gap between the data area and the axes) and
  575        * sends a {@link PlotChangeEvent} to all registered listeners.
  576        *
  577        * @param offset  the offset (<code>null</code> not permitted).
  578        *
  579        * @see #getAxisOffset()
  580        */
  581       public void setAxisOffset(RectangleInsets offset) {
  582           if (offset == null) {
  583               throw new IllegalArgumentException("Null 'offset' argument.");
  584           }
  585           this.axisOffset = offset;
  586           fireChangeEvent();
  587       }
  588   
  589       /**
  590        * Returns the domain axis for the plot.  If the domain axis for this plot
  591        * is <code>null</code>, then the method will return the parent plot's
  592        * domain axis (if there is a parent plot).
  593        *
  594        * @return The domain axis (<code>null</code> permitted).
  595        *
  596        * @see #setDomainAxis(CategoryAxis)
  597        */
  598       public CategoryAxis getDomainAxis() {
  599           return getDomainAxis(0);
  600       }
  601   
  602       /**
  603        * Returns a domain axis.
  604        *
  605        * @param index  the axis index.
  606        *
  607        * @return The axis (<code>null</code> possible).
  608        *
  609        * @see #setDomainAxis(int, CategoryAxis)
  610        */
  611       public CategoryAxis getDomainAxis(int index) {
  612           CategoryAxis result = null;
  613           if (index < this.domainAxes.size()) {
  614               result = (CategoryAxis) this.domainAxes.get(index);
  615           }
  616           if (result == null) {
  617               Plot parent = getParent();
  618               if (parent instanceof CategoryPlot) {
  619                   CategoryPlot cp = (CategoryPlot) parent;
  620                   result = cp.getDomainAxis(index);
  621               }
  622           }
  623           return result;
  624       }
  625   
  626       /**
  627        * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
  628        * all registered listeners.
  629        *
  630        * @param axis  the axis (<code>null</code> permitted).
  631        *
  632        * @see #getDomainAxis()
  633        */
  634       public void setDomainAxis(CategoryAxis axis) {
  635           setDomainAxis(0, axis);
  636       }
  637   
  638       /**
  639        * Sets a domain axis and sends a {@link PlotChangeEvent} to all
  640        * registered listeners.
  641        *
  642        * @param index  the axis index.
  643        * @param axis  the axis (<code>null</code> permitted).
  644        *
  645        * @see #getDomainAxis(int)
  646        */
  647       public void setDomainAxis(int index, CategoryAxis axis) {
  648           setDomainAxis(index, axis, true);
  649       }
  650   
  651       /**
  652        * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
  653        * all registered listeners.
  654        *
  655        * @param index  the axis index.
  656        * @param axis  the axis (<code>null</code> permitted).
  657        * @param notify  notify listeners?
  658        */
  659       public void setDomainAxis(int index, CategoryAxis axis, boolean notify) {
  660           CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
  661           if (existing != null) {
  662               existing.removeChangeListener(this);
  663           }
  664           if (axis != null) {
  665               axis.setPlot(this);
  666           }
  667           this.domainAxes.set(index, axis);
  668           if (axis != null) {
  669               axis.configure();
  670               axis.addChangeListener(this);
  671           }
  672           if (notify) {
  673               fireChangeEvent();
  674           }
  675       }
  676   
  677       /**
  678        * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
  679        * to all registered listeners.
  680        *
  681        * @param axes  the axes (<code>null</code> not permitted).
  682        *
  683        * @see #setRangeAxes(ValueAxis[])
  684        */
  685       public void setDomainAxes(CategoryAxis[] axes) {
  686           for (int i = 0; i < axes.length; i++) {
  687               setDomainAxis(i, axes[i], false);
  688           }
  689           fireChangeEvent();
  690       }
  691   
  692       /**
  693        * Returns the index of the specified axis, or <code>-1</code> if the axis
  694        * is not assigned to the plot.
  695        *
  696        * @param axis  the axis (<code>null</code> not permitted).
  697        *
  698        * @return The axis index.
  699        *
  700        * @see #getDomainAxis(int)
  701        * @see #getRangeAxisIndex(ValueAxis)
  702        *
  703        * @since 1.0.3
  704        */
  705       public int getDomainAxisIndex(CategoryAxis axis) {
  706           if (axis == null) {
  707               throw new IllegalArgumentException("Null 'axis' argument.");
  708           }
  709           return this.domainAxes.indexOf(axis);
  710       }
  711   
  712       /**
  713        * Returns the domain axis location for the primary domain axis.
  714        *
  715        * @return The location (never <code>null</code>).
  716        *
  717        * @see #getRangeAxisLocation()
  718        */
  719       public AxisLocation getDomainAxisLocation() {
  720           return getDomainAxisLocation(0);
  721       }
  722   
  723       /**
  724        * Returns the location for a domain axis.
  725        *
  726        * @param index  the axis index.
  727        *
  728        * @return The location.
  729        *
  730        * @see #setDomainAxisLocation(int, AxisLocation)
  731        */
  732       public AxisLocation getDomainAxisLocation(int index) {
  733           AxisLocation result = null;
  734           if (index < this.domainAxisLocations.size()) {
  735               result = (AxisLocation) this.domainAxisLocations.get(index);
  736           }
  737           if (result == null) {
  738               result = AxisLocation.getOpposite(getDomainAxisLocation(0));
  739           }
  740           return result;
  741       }
  742   
  743       /**
  744        * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
  745        * to all registered listeners.
  746        *
  747        * @param location  the axis location (<code>null</code> not permitted).
  748        *
  749        * @see #getDomainAxisLocation()
  750        * @see #setDomainAxisLocation(int, AxisLocation)
  751        */
  752       public void setDomainAxisLocation(AxisLocation location) {
  753           // delegate...
  754           setDomainAxisLocation(0, location, true);
  755       }
  756   
  757       /**
  758        * Sets the location of the domain axis and, if requested, sends a
  759        * {@link PlotChangeEvent} to all registered listeners.
  760        *
  761        * @param location  the axis location (<code>null</code> not permitted).
  762        * @param notify  a flag that controls whether listeners are notified.
  763        */
  764       public void setDomainAxisLocation(AxisLocation location, boolean notify) {
  765           // delegate...
  766           setDomainAxisLocation(0, location, notify);
  767       }
  768   
  769       /**
  770        * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
  771        * to all registered listeners.
  772        *
  773        * @param index  the axis index.
  774        * @param location  the location.
  775        *
  776        * @see #getDomainAxisLocation(int)
  777        * @see #setRangeAxisLocation(int, AxisLocation)
  778        */
  779       public void setDomainAxisLocation(int index, AxisLocation location) {
  780           // delegate...
  781           setDomainAxisLocation(index, location, true);
  782       }
  783   
  784       /**
  785        * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
  786        * to all registered listeners.
  787        *
  788        * @param index  the axis index.
  789        * @param location  the location.
  790        * @param notify  notify listeners?
  791        *
  792        * @since 1.0.5
  793        *
  794        * @see #getDomainAxisLocation(int)
  795        * @see #setRangeAxisLocation(int, AxisLocation, boolean)
  796        */
  797       public void setDomainAxisLocation(int index, AxisLocation location,
  798               boolean notify) {
  799           if (index == 0 && location == null) {
  800               throw new IllegalArgumentException(
  801                       "Null 'location' for index 0 not permitted.");
  802           }
  803           this.domainAxisLocations.set(index, location);
  804           if (notify) {
  805               fireChangeEvent();
  806           }
  807       }
  808   
  809       /**
  810        * Returns the domain axis edge.  This is derived from the axis location
  811        * and the plot orientation.
  812        *
  813        * @return The edge (never <code>null</code>).
  814        */
  815       public RectangleEdge getDomainAxisEdge() {
  816           return getDomainAxisEdge(0);
  817       }
  818   
  819       /**
  820        * Returns the edge for a domain axis.
  821        *
  822        * @param index  the axis index.
  823        *
  824        * @return The edge (never <code>null</code>).
  825        */
  826       public RectangleEdge getDomainAxisEdge(int index) {
  827           RectangleEdge result = null;
  828           AxisLocation location = getDomainAxisLocation(index);
  829           if (location != null) {
  830               result = Plot.resolveDomainAxisLocation(location, this.orientation);
  831           }
  832           else {
  833               result = RectangleEdge.opposite(getDomainAxisEdge(0));
  834           }
  835           return result;
  836       }
  837   
  838       /**
  839        * Returns the number of domain axes.
  840        *
  841        * @return The axis count.
  842        */
  843       public int getDomainAxisCount() {
  844           return this.domainAxes.size();
  845       }
  846   
  847       /**
  848        * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
  849        * to all registered listeners.
  850        */
  851       public void clearDomainAxes() {
  852           for (int i = 0; i < this.domainAxes.size(); i++) {
  853               CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
  854               if (axis != null) {
  855                   axis.removeChangeListener(this);
  856               }
  857           }
  858           this.domainAxes.clear();
  859           fireChangeEvent();
  860       }
  861   
  862       /**
  863        * Configures the domain axes.
  864        */
  865       public void configureDomainAxes() {
  866           for (int i = 0; i < this.domainAxes.size(); i++) {
  867               CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
  868               if (axis != null) {
  869                   axis.configure();
  870               }
  871           }
  872       }
  873   
  874       /**
  875        * Returns the range axis for the plot.  If the range axis for this plot is
  876        * null, then the method will return the parent plot's range axis (if there
  877        * is a parent plot).
  878        *
  879        * @return The range axis (possibly <code>null</code>).
  880        */
  881       public ValueAxis getRangeAxis() {
  882           return getRangeAxis(0);
  883       }
  884   
  885       /**
  886        * Returns a range axis.
  887        *
  888        * @param index  the axis index.
  889        *
  890        * @return The axis (<code>null</code> possible).
  891        */
  892       public ValueAxis getRangeAxis(int index) {
  893           ValueAxis result = null;
  894           if (index < this.rangeAxes.size()) {
  895               result = (ValueAxis) this.rangeAxes.get(index);
  896           }
  897           if (result == null) {
  898               Plot parent = getParent();
  899               if (parent instanceof CategoryPlot) {
  900                   CategoryPlot cp = (CategoryPlot) parent;
  901                   result = cp.getRangeAxis(index);
  902               }
  903           }
  904           return result;
  905       }
  906   
  907       /**
  908        * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
  909        * all registered listeners.
  910        *
  911        * @param axis  the axis (<code>null</code> permitted).
  912        */
  913       public void setRangeAxis(ValueAxis axis) {
  914           setRangeAxis(0, axis);
  915       }
  916   
  917       /**
  918        * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
  919        * listeners.
  920        *
  921        * @param index  the axis index.
  922        * @param axis  the axis.
  923        */
  924       public void setRangeAxis(int index, ValueAxis axis) {
  925           setRangeAxis(index, axis, true);
  926       }
  927   
  928       /**
  929        * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
  930        * all registered listeners.
  931        *
  932        * @param index  the axis index.
  933        * @param axis  the axis.
  934        * @param notify  notify listeners?
  935        */
  936       public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
  937           ValueAxis existing = (ValueAxis) this.rangeAxes.get(index);
  938           if (existing != null) {
  939               existing.removeChangeListener(this);
  940           }
  941           if (axis != null) {
  942               axis.setPlot(this);
  943           }
  944           this.rangeAxes.set(index, axis);
  945           if (axis != null) {
  946               axis.configure();
  947               axis.addChangeListener(this);
  948           }
  949           if (notify) {
  950               fireChangeEvent();
  951           }
  952       }
  953   
  954       /**
  955        * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
  956        * to all registered listeners.
  957        *
  958        * @param axes  the axes (<code>null</code> not permitted).
  959        *
  960        * @see #setDomainAxes(CategoryAxis[])
  961        */
  962       public void setRangeAxes(ValueAxis[] axes) {
  963           for (int i = 0; i < axes.length; i++) {
  964               setRangeAxis(i, axes[i], false);
  965           }
  966           fireChangeEvent();
  967       }
  968   
  969       /**
  970        * Returns the index of the specified axis, or <code>-1</code> if the axis
  971        * is not assigned to the plot.
  972        *
  973        * @param axis  the axis (<code>null</code> not permitted).
  974        *
  975        * @return The axis index.
  976        *
  977        * @see #getRangeAxis(int)
  978        * @see #getDomainAxisIndex(CategoryAxis)
  979        *
  980        * @since 1.0.7
  981        */
  982       public int getRangeAxisIndex(ValueAxis axis) {
  983           if (axis == null) {
  984               throw new IllegalArgumentException("Null 'axis' argument.");
  985           }
  986           int result = this.rangeAxes.indexOf(axis);
  987           if (result < 0) { // try the parent plot
  988               Plot parent = getParent();
  989               if (parent instanceof CategoryPlot) {
  990                   CategoryPlot p = (CategoryPlot) parent;
  991                   result = p.getRangeAxisIndex(axis);
  992               }
  993           }
  994           return result;
  995       }
  996   
  997       /**
  998        * Returns the range axis location.
  999        *
 1000        * @return The location (never <code>null</code>).
 1001        */
 1002       public AxisLocation getRangeAxisLocation() {
 1003           return getRangeAxisLocation(0);
 1004       }
 1005   
 1006       /**
 1007        * Returns the location for a range axis.
 1008        *
 1009        * @param index  the axis index.
 1010        *
 1011        * @return The location.
 1012        *
 1013        * @see #setRangeAxisLocation(int, AxisLocation)
 1014        */
 1015       public AxisLocation getRangeAxisLocation(int index) {
 1016           AxisLocation result = null;
 1017           if (index < this.rangeAxisLocations.size()) {
 1018               result = (AxisLocation) this.rangeAxisLocations.get(index);
 1019           }
 1020           if (result == null) {
 1021               result = AxisLocation.getOpposite(getRangeAxisLocation(0));
 1022           }
 1023           return result;
 1024       }
 1025   
 1026       /**
 1027        * Sets the location of the range axis and sends a {@link PlotChangeEvent}
 1028        * to all registered listeners.
 1029        *
 1030        * @param location  the location (<code>null</code> not permitted).
 1031        *
 1032        * @see #setRangeAxisLocation(AxisLocation, boolean)
 1033        * @see #setDomainAxisLocation(AxisLocation)
 1034        */
 1035       public void setRangeAxisLocation(AxisLocation location) {
 1036           // defer argument checking...
 1037           setRangeAxisLocation(location, true);
 1038       }
 1039   
 1040       /**
 1041        * Sets the location of the range axis and, if requested, sends a
 1042        * {@link PlotChangeEvent} to all registered listeners.
 1043        *
 1044        * @param location  the location (<code>null</code> not permitted).
 1045        * @param notify  notify listeners?
 1046        *
 1047        * @see #setDomainAxisLocation(AxisLocation, boolean)
 1048        */
 1049       public void setRangeAxisLocation(AxisLocation location, boolean notify) {
 1050           setRangeAxisLocation(0, location, notify);
 1051       }
 1052   
 1053       /**
 1054        * Sets the location for a range axis and sends a {@link PlotChangeEvent}
 1055        * to all registered listeners.
 1056        *
 1057        * @param index  the axis index.
 1058        * @param location  the location.
 1059        *
 1060        * @see #getRangeAxisLocation(int)
 1061        * @see #setRangeAxisLocation(int, AxisLocation, boolean)
 1062        */
 1063       public void setRangeAxisLocation(int index, AxisLocation location) {
 1064           setRangeAxisLocation(index, location, true);
 1065       }
 1066   
 1067       /**
 1068        * Sets the location for a range axis and sends a {@link PlotChangeEvent}
 1069        * to all registered listeners.
 1070        *
 1071        * @param index  the axis index.
 1072        * @param location  the location.
 1073        * @param notify  notify listeners?
 1074        *
 1075        * @see #getRangeAxisLocation(int)
 1076        * @see #setDomainAxisLocation(int, AxisLocation, boolean)
 1077        */
 1078       public void setRangeAxisLocation(int index, AxisLocation location,
 1079                                        boolean notify) {
 1080           if (index == 0 && location == null) {
 1081               throw new IllegalArgumentException(
 1082                       "Null 'location' for index 0 not permitted.");
 1083           }
 1084           this.rangeAxisLocations.set(index, location);
 1085           if (notify) {
 1086               fireChangeEvent();
 1087           }
 1088       }
 1089   
 1090       /**
 1091        * Returns the edge where the primary range axis is located.
 1092        *
 1093        * @return The edge (never <code>null</code>).
 1094        */
 1095       public RectangleEdge getRangeAxisEdge() {
 1096           return getRangeAxisEdge(0);
 1097       }
 1098   
 1099       /**
 1100        * Returns the edge for a range axis.
 1101        *
 1102        * @param index  the axis index.
 1103        *
 1104        * @return The edge.
 1105        */
 1106       public RectangleEdge getRangeAxisEdge(int index) {
 1107           AxisLocation location = getRangeAxisLocation(index);
 1108           RectangleEdge result = Plot.resolveRangeAxisLocation(location,
 1109                   this.orientation);
 1110           if (result == null) {
 1111               result = RectangleEdge.opposite(getRangeAxisEdge(0));
 1112           }
 1113           return result;
 1114       }
 1115   
 1116       /**
 1117        * Returns the number of range axes.
 1118        *
 1119        * @return The axis count.
 1120        */
 1121       public int getRangeAxisCount() {
 1122           return this.rangeAxes.size();
 1123       }
 1124   
 1125       /**
 1126        * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
 1127        * to all registered listeners.
 1128        */
 1129       public void clearRangeAxes() {
 1130           for (int i = 0; i < this.rangeAxes.size(); i++) {
 1131               ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
 1132               if (axis != null) {
 1133                   axis.removeChangeListener(this);
 1134               }
 1135           }
 1136           this.rangeAxes.clear();
 1137           fireChangeEvent();
 1138       }
 1139   
 1140       /**
 1141        * Configures the range axes.
 1142        */
 1143       public void configureRangeAxes() {
 1144           for (int i = 0; i < this.rangeAxes.size(); i++) {
 1145               ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
 1146               if (axis != null) {
 1147                   axis.configure();
 1148               }
 1149           }
 1150       }
 1151   
 1152       /**
 1153        * Returns the primary dataset for the plot.
 1154        *
 1155        * @return The primary dataset (possibly <code>null</code>).
 1156        *
 1157        * @see #setDataset(CategoryDataset)
 1158        */
 1159       public CategoryDataset getDataset() {
 1160           return getDataset(0);
 1161       }
 1162   
 1163       /**
 1164        * Returns the dataset at the given index.
 1165        *
 1166        * @param index  the dataset index.
 1167        *
 1168        * @return The dataset (possibly <code>null</code>).
 1169        *
 1170        * @see #setDataset(int, CategoryDataset)
 1171        */
 1172       public CategoryDataset getDataset(int index) {
 1173           CategoryDataset result = null;
 1174           if (this.datasets.size() > index) {
 1175               result = (CategoryDataset) this.datasets.get(index);
 1176           }
 1177           return result;
 1178       }
 1179   
 1180       /**
 1181        * Sets the dataset for the plot, replacing the existing dataset, if there
 1182        * is one.  This method also calls the
 1183        * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
 1184        * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
 1185        * registered listeners.
 1186        *
 1187        * @param dataset  the dataset (<code>null</code> permitted).
 1188        *
 1189        * @see #getDataset()
 1190        */
 1191       public void setDataset(CategoryDataset dataset) {
 1192           setDataset(0, dataset);
 1193       }
 1194   
 1195       /**
 1196        * Sets a dataset for the plot.
 1197        *
 1198        * @param index  the dataset index.
 1199        * @param dataset  the dataset (<code>null</code> permitted).
 1200        *
 1201        * @see #getDataset(int)
 1202        */
 1203       public void setDataset(int index, CategoryDataset dataset) {
 1204   
 1205           CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
 1206           if (existing != null) {
 1207               existing.removeChangeListener(this);
 1208           }
 1209           this.datasets.set(index, dataset);
 1210           if (dataset != null) {
 1211               dataset.addChangeListener(this);
 1212           }
 1213   
 1214           // send a dataset change event to self...
 1215           DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
 1216           datasetChanged(event);
 1217   
 1218       }
 1219   
 1220       /**
 1221        * Returns the number of datasets.
 1222        *
 1223        * @return The number of datasets.
 1224        *
 1225        * @since 1.0.2
 1226        */
 1227       public int getDatasetCount() {
 1228           return this.datasets.size();
 1229       }
 1230   
 1231       /**
 1232        * Maps a dataset to a particular domain axis.
 1233        *
 1234        * @param index  the dataset index (zero-based).
 1235        * @param axisIndex  the axis index (zero-based).
 1236        *
 1237        * @see #getDomainAxisForDataset(int)
 1238        */
 1239       public void mapDatasetToDomainAxis(int index, int axisIndex) {
 1240           this.datasetToDomainAxisMap.set(index, new Integer(axisIndex));
 1241           // fake a dataset change event to update axes...
 1242           datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
 1243       }
 1244   
 1245       /**
 1246        * Returns the domain axis for a dataset.  You can change the axis for a
 1247        * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
 1248        *
 1249        * @param index  the dataset index.
 1250        *
 1251        * @return The domain axis.
 1252        *
 1253        * @see #mapDatasetToDomainAxis(int, int)
 1254        */
 1255       public CategoryAxis getDomainAxisForDataset(int index) {
 1256           CategoryAxis result = getDomainAxis();
 1257           Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(index);
 1258           if (axisIndex != null) {
 1259               result = getDomainAxis(axisIndex.intValue());
 1260           }
 1261           return result;
 1262       }
 1263   
 1264       /**
 1265        * Maps a dataset to a particular range axis.
 1266        *
 1267        * @param index  the dataset index (zero-based).
 1268        * @param axisIndex  the axis index (zero-based).
 1269        *
 1270        * @see #getRangeAxisForDataset(int)
 1271        */
 1272       public void mapDatasetToRangeAxis(int index, int axisIndex) {
 1273           this.datasetToRangeAxisMap.set(index, new Integer(axisIndex));
 1274           // fake a dataset change event to update axes...
 1275           datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
 1276       }
 1277   
 1278       /**
 1279        * Returns the range axis for a dataset.  You can change the axis for a
 1280        * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
 1281        *
 1282        * @param index  the dataset index.
 1283        *
 1284        * @return The range axis.
 1285        *
 1286        * @see #mapDatasetToRangeAxis(int, int)
 1287        */
 1288       public ValueAxis getRangeAxisForDataset(int index) {
 1289           ValueAxis result = getRangeAxis();
 1290           Integer axisIndex = (Integer) this.datasetToRangeAxisMap.get(index);
 1291           if (axisIndex != null) {
 1292               result = getRangeAxis(axisIndex.intValue());
 1293           }
 1294           return result;
 1295       }
 1296   
 1297       /**
 1298        * Returns a reference to the renderer for the plot.
 1299        *
 1300        * @return The renderer.
 1301        *
 1302        * @see #setRenderer(CategoryItemRenderer)
 1303        */
 1304       public CategoryItemRenderer getRenderer() {
 1305           return getRenderer(0);
 1306       }
 1307   
 1308       /**
 1309        * Returns the renderer at the given index.
 1310        *
 1311        * @param index  the renderer index.
 1312        *
 1313        * @return The renderer (possibly <code>null</code>).
 1314        *
 1315        * @see #setRenderer(int, CategoryItemRenderer)
 1316        */
 1317       public CategoryItemRenderer getRenderer(int index) {
 1318           CategoryItemRenderer result = null;
 1319           if (this.renderers.size() > index) {
 1320               result = (CategoryItemRenderer) this.renderers.get(index);
 1321           }
 1322           return result;
 1323       }
 1324   
 1325       /**
 1326        * Sets the renderer at index 0 (sometimes referred to as the "primary"
 1327        * renderer) and sends a {@link PlotChangeEvent} to all registered
 1328        * listeners.
 1329        *
 1330        * @param renderer  the renderer (<code>null</code> permitted.
 1331        *
 1332        * @see #getRenderer()
 1333        */
 1334       public void setRenderer(CategoryItemRenderer renderer) {
 1335           setRenderer(0, renderer, true);
 1336       }
 1337   
 1338       /**
 1339        * Sets the renderer at index 0 (sometimes referred to as the "primary"
 1340        * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
 1341        * registered listeners.
 1342        * <p>
 1343        * You can set the renderer to <code>null</code>, but this is not
 1344        * recommended because:
 1345        * <ul>
 1346        *   <li>no data will be displayed;</li>
 1347        *   <li>the plot background will not be painted;</li>
 1348        * </ul>
 1349        *
 1350        * @param renderer  the renderer (<code>null</code> permitted).
 1351        * @param notify  notify listeners?
 1352        *
 1353        * @see #getRenderer()
 1354        */
 1355       public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
 1356           setRenderer(0, renderer, notify);
 1357       }
 1358   
 1359       /**
 1360        * Sets the renderer at the specified index and sends a
 1361        * {@link PlotChangeEvent} to all registered listeners.
 1362        *
 1363        * @param index  the index.
 1364        * @param renderer  the renderer (<code>null</code> permitted).
 1365        *
 1366        * @see #getRenderer(int)
 1367        * @see #setRenderer(int, CategoryItemRenderer, boolean)
 1368        */
 1369       public void setRenderer(int index, CategoryItemRenderer renderer) {
 1370           setRenderer(index, renderer, true);
 1371       }
 1372   
 1373       /**
 1374        * Sets a renderer.  A {@link PlotChangeEvent} is sent to all registered
 1375        * listeners.
 1376        *
 1377        * @param index  the index.
 1378        * @param renderer  the renderer (<code>null</code> permitted).
 1379        * @param notify  notify listeners?
 1380        *
 1381        * @see #getRenderer(int)
 1382        */
 1383       public void setRenderer(int index, CategoryItemRenderer renderer,
 1384                               boolean notify) {
 1385   
 1386           // stop listening to the existing renderer...
 1387           CategoryItemRenderer existing
 1388               = (CategoryItemRenderer) this.renderers.get(index);
 1389           if (existing != null) {
 1390               existing.removeChangeListener(this);
 1391           }
 1392   
 1393           // register the new renderer...
 1394           this.renderers.set(index, renderer);
 1395           if (renderer != null) {
 1396               renderer.setPlot(this);
 1397               renderer.addChangeListener(this);
 1398           }
 1399   
 1400           configureDomainAxes();
 1401           configureRangeAxes();
 1402   
 1403           if (notify) {
 1404               fireChangeEvent();
 1405           }
 1406       }
 1407   
 1408       /**
 1409        * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
 1410        * to all registered listeners.
 1411        *
 1412        * @param renderers  the renderers.
 1413        */
 1414       public void setRenderers(CategoryItemRenderer[] renderers) {
 1415           for (int i = 0; i < renderers.length; i++) {
 1416               setRenderer(i, renderers[i], false);
 1417           }
 1418           fireChangeEvent();
 1419       }
 1420   
 1421       /**
 1422        * Returns the renderer for the specified dataset.  If the dataset doesn't
 1423        * belong to the plot, this method will return <code>null</code>.
 1424        *
 1425        * @param dataset  the dataset (<code>null</code> permitted).
 1426        *
 1427        * @return The renderer (possibly <code>null</code>).
 1428        */
 1429       public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
 1430           CategoryItemRenderer result = null;
 1431           for (int i = 0; i < this.datasets.size(); i++) {
 1432               if (this.datasets.get(i) == dataset) {
 1433                   result = (CategoryItemRenderer) this.renderers.get(i);
 1434                   break;
 1435               }
 1436           }
 1437           return result;
 1438       }
 1439   
 1440       /**
 1441        * Returns the index of the specified renderer, or <code>-1</code> if the
 1442        * renderer is not assigned to this plot.
 1443        *
 1444        * @param renderer  the renderer (<code>null</code> permitted).
 1445        *
 1446        * @return The renderer index.
 1447        */
 1448       public int getIndexOf(CategoryItemRenderer renderer) {
 1449           return this.renderers.indexOf(renderer);
 1450       }
 1451   
 1452       /**
 1453        * Returns the dataset rendering order.
 1454        *
 1455        * @return The order (never <code>null</code>).
 1456        *
 1457        * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
 1458        */
 1459       public DatasetRenderingOrder getDatasetRenderingOrder() {
 1460           return this.renderingOrder;
 1461       }
 1462   
 1463       /**
 1464        * Sets the rendering order and sends a {@link PlotChangeEvent} to all
 1465        * registered listeners.  By default, the plot renders the primary dataset
 1466        * last (so that the primary dataset overlays the secondary datasets).  You
 1467        * can reverse this if you want to.
 1468        *
 1469        * @param order  the rendering order (<code>null</code> not permitted).
 1470        *
 1471        * @see #getDatasetRenderingOrder()
 1472        */
 1473       public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
 1474           if (order == null) {
 1475               throw new IllegalArgumentException("Null 'order' argument.");
 1476           }
 1477           this.renderingOrder = order;
 1478           fireChangeEvent();
 1479       }
 1480   
 1481       /**
 1482        * Returns the order in which the columns are rendered.  The default value
 1483        * is <code>SortOrder.ASCENDING</code>.
 1484        *
 1485        * @return The column rendering order (never <code>null</code).
 1486        *
 1487        * @see #setColumnRenderingOrder(SortOrder)
 1488        */
 1489       public SortOrder getColumnRenderingOrder() {
 1490           return this.columnRenderingOrder;
 1491       }
 1492   
 1493       /**
 1494        * Sets the column order in which the items in each dataset should be
 1495        * rendered and sends a {@link PlotChangeEvent} to all registered
 1496        * listeners.  Note that this affects the order in which items are drawn,
 1497        * NOT their position in the chart.
 1498        *
 1499        * @param order  the order (<code>null</code> not permitted).
 1500        *
 1501        * @see #getColumnRenderingOrder()
 1502        * @see #setRowRenderingOrder(SortOrder)
 1503        */
 1504       public void setColumnRenderingOrder(SortOrder order) {
 1505           if (order == null) {
 1506               throw new IllegalArgumentException("Null 'order' argument.");
 1507           }
 1508           this.columnRenderingOrder = order;
 1509           fireChangeEvent();
 1510       }
 1511   
 1512       /**
 1513        * Returns the order in which the rows should be rendered.  The default
 1514        * value is <code>SortOrder.ASCENDING</code>.
 1515        *
 1516        * @return The order (never <code>null</code>).
 1517        *
 1518        * @see #setRowRenderingOrder(SortOrder)
 1519        */
 1520       public SortOrder getRowRenderingOrder() {
 1521           return this.rowRenderingOrder;
 1522       }
 1523   
 1524       /**
 1525        * Sets the row order in which the items in each dataset should be
 1526        * rendered and sends a {@link PlotChangeEvent} to all registered
 1527        * listeners.  Note that this affects the order in which items are drawn,
 1528        * NOT their position in the chart.
 1529        *
 1530        * @param order  the order (<code>null</code> not permitted).
 1531        *
 1532        * @see #getRowRenderingOrder()
 1533        * @see #setColumnRenderingOrder(SortOrder)
 1534        */
 1535       public void setRowRenderingOrder(SortOrder order) {
 1536           if (order == null) {
 1537               throw new IllegalArgumentException("Null 'order' argument.");
 1538           }
 1539           this.rowRenderingOrder = order;
 1540           fireChangeEvent();
 1541       }
 1542   
 1543       /**
 1544        * Returns the flag that controls whether the domain grid-lines are visible.
 1545        *
 1546        * @return The <code>true</code> or <code>false</code>.
 1547        *
 1548        * @see #setDomainGridlinesVisible(boolean)
 1549        */
 1550       public boolean isDomainGridlinesVisible() {
 1551           return this.domainGridlinesVisible;
 1552       }
 1553   
 1554       /**
 1555        * Sets the flag that controls whether or not grid-lines are drawn against
 1556        * the domain axis.
 1557        * <p>
 1558        * If the flag value changes, a {@link PlotChangeEvent} is sent to all
 1559        * registered listeners.
 1560        *
 1561        * @param visible  the new value of the flag.
 1562        *
 1563        * @see #isDomainGridlinesVisible()
 1564        */
 1565       public void setDomainGridlinesVisible(boolean visible) {
 1566           if (this.domainGridlinesVisible != visible) {
 1567               this.domainGridlinesVisible = visible;
 1568               fireChangeEvent();
 1569           }
 1570       }
 1571   
 1572       /**
 1573        * Returns the position used for the domain gridlines.
 1574        *
 1575        * @return The gridline position (never <code>null</code>).
 1576        *
 1577        * @see #setDomainGridlinePosition(CategoryAnchor)
 1578        */
 1579       public CategoryAnchor getDomainGridlinePosition() {
 1580           return this.domainGridlinePosition;
 1581       }
 1582   
 1583       /**
 1584        * Sets the position used for the domain gridlines and sends a
 1585        * {@link PlotChangeEvent} to all registered listeners.
 1586        *
 1587        * @param position  the position (<code>null</code> not permitted).
 1588        *
 1589        * @see #getDomainGridlinePosition()
 1590        */
 1591       public void setDomainGridlinePosition(CategoryAnchor position) {
 1592           if (position == null) {
 1593               throw new IllegalArgumentException("Null 'position' argument.");
 1594           }
 1595           this.domainGridlinePosition = position;
 1596           fireChangeEvent();
 1597       }
 1598   
 1599       /**
 1600        * Returns the stroke used to draw grid-lines against the domain axis.
 1601        *
 1602        * @return The stroke (never <code>null</code>).
 1603        *
 1604        * @see #setDomainGridlineStroke(Stroke)
 1605        */
 1606       public Stroke getDomainGridlineStroke() {
 1607           return this.domainGridlineStroke;
 1608       }
 1609   
 1610       /**
 1611        * Sets the stroke used to draw grid-lines against the domain axis and
 1612        * sends a {@link PlotChangeEvent} to all registered listeners.
 1613        *
 1614        * @param stroke  the stroke (<code>null</code> not permitted).
 1615        *
 1616        * @see #getDomainGridlineStroke()
 1617        */
 1618       public void setDomainGridlineStroke(Stroke stroke) {
 1619           if (stroke == null) {
 1620               throw new IllegalArgumentException("Null 'stroke' not permitted.");
 1621           }
 1622           this.domainGridlineStroke = stroke;
 1623           fireChangeEvent();
 1624       }
 1625   
 1626       /**
 1627        * Returns the paint used to draw grid-lines against the domain axis.
 1628        *
 1629        * @return The paint (never <code>null</code>).
 1630        *
 1631        * @see #setDomainGridlinePaint(Paint)
 1632        */
 1633       public Paint getDomainGridlinePaint() {
 1634           return this.domainGridlinePaint;
 1635       }
 1636   
 1637       /**
 1638        * Sets the paint used to draw the grid-lines (if any) against the domain
 1639        * axis and sends a {@link PlotChangeEvent} to all registered listeners.
 1640        *
 1641        * @param paint  the paint (<code>null</code> not permitted).
 1642        *
 1643        * @see #getDomainGridlinePaint()
 1644        */
 1645       public void setDomainGridlinePaint(Paint paint) {
 1646           if (paint == null) {
 1647               throw new IllegalArgumentException("Null 'paint' argument.");
 1648           }
 1649           this.domainGridlinePaint = paint;
 1650           fireChangeEvent();
 1651       }
 1652   
 1653       /**
 1654        * Returns the flag that controls whether the range grid-lines are visible.
 1655        *
 1656        * @return The flag.
 1657        *
 1658        * @see #setRangeGridlinesVisible(boolean)
 1659        */
 1660       public boolean isRangeGridlinesVisible() {
 1661           return this.rangeGridlinesVisible;
 1662       }
 1663   
 1664       /**
 1665        * Sets the flag that controls whether or not grid-lines are drawn against
 1666        * the range axis.  If the flag changes value, a {@link PlotChangeEvent} is
 1667        * sent to all registered listeners.
 1668        *
 1669        * @param visible  the new value of the flag.
 1670        *
 1671        * @see #isRangeGridlinesVisible()
 1672        */
 1673       public void setRangeGridlinesVisible(boolean visible) {
 1674           if (this.rangeGridlinesVisible != visible) {
 1675               this.rangeGridlinesVisible = visible;
 1676               fireChangeEvent();
 1677           }
 1678       }
 1679   
 1680       /**
 1681        * Returns the stroke used to draw the grid-lines against the range axis.
 1682        *
 1683        * @return The stroke (never <code>null</code>).
 1684        *
 1685        * @see #setRangeGridlineStroke(Stroke)
 1686        */
 1687       public Stroke getRangeGridlineStroke() {
 1688           return this.rangeGridlineStroke;
 1689       }
 1690   
 1691       /**
 1692        * Sets the stroke used to draw the grid-lines against the range axis and
 1693        * sends a {@link PlotChangeEvent} to all registered listeners.
 1694        *
 1695        * @param stroke  the stroke (<code>null</code> not permitted).
 1696        *
 1697        * @see #getRangeGridlineStroke()
 1698        */
 1699       public void setRangeGridlineStroke(Stroke stroke) {
 1700           if (stroke == null) {
 1701               throw new IllegalArgumentException("Null 'stroke' argument.");
 1702           }
 1703           this.rangeGridlineStroke = stroke;
 1704           fireChangeEvent();
 1705       }
 1706   
 1707       /**
 1708        * Returns the paint used to draw the grid-lines against the range axis.
 1709        *
 1710        * @return The paint (never <code>null</code>).
 1711        *
 1712        * @see #setRangeGridlinePaint(Paint)
 1713        */
 1714       public Paint getRangeGridlinePaint() {
 1715           return this.rangeGridlinePaint;
 1716       }
 1717   
 1718       /**
 1719        * Sets the paint used to draw the grid lines against the range axis and
 1720        * sends a {@link PlotChangeEvent} to all registered listeners.
 1721        *
 1722        * @param paint  the paint (<code>null</code> not permitted).
 1723        *
 1724        * @see #getRangeGridlinePaint()
 1725        */
 1726       public void setRangeGridlinePaint(Paint paint) {
 1727           if (paint == null) {
 1728               throw new IllegalArgumentException("Null 'paint' argument.");
 1729           }
 1730           this.rangeGridlinePaint = paint;
 1731           fireChangeEvent();
 1732       }
 1733   
 1734       /**
 1735        * Returns the fixed legend items, if any.
 1736        *
 1737        * @return The legend items (possibly <code>null</code>).
 1738        *
 1739        * @see #setFixedLegendItems(LegendItemCollection)
 1740        */
 1741       public LegendItemCollection getFixedLegendItems() {
 1742           return this.fixedLegendItems;
 1743       }
 1744   
 1745       /**
 1746        * Sets the fixed legend items for the plot.  Leave this set to
 1747        * <code>null</code> if you prefer the legend items to be created
 1748        * automatically.
 1749        *
 1750        * @param items  the legend items (<code>null</code> permitted).
 1751        *
 1752        * @see #getFixedLegendItems()
 1753        */
 1754       public void setFixedLegendItems(LegendItemCollection items) {
 1755           this.fixedLegendItems = items;
 1756           fireChangeEvent();
 1757       }
 1758   
 1759       /**
 1760        * Returns the legend items for the plot.  By default, this method creates
 1761        * a legend item for each series in each of the datasets.  You can change
 1762        * this behaviour by overriding this method.
 1763        *
 1764        * @return The legend items.
 1765        */
 1766       public LegendItemCollection getLegendItems() {
 1767           LegendItemCollection result = this.fixedLegendItems;
 1768           if (result == null) {
 1769               result = new LegendItemCollection();
 1770               // get the legend items for the datasets...
 1771               int count = this.datasets.size();
 1772               for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
 1773                   CategoryDataset dataset = getDataset(datasetIndex);
 1774                   if (dataset != null) {
 1775                       CategoryItemRenderer renderer = getRenderer(datasetIndex);
 1776                       if (renderer != null) {
 1777                           int seriesCount = dataset.getRowCount();
 1778                           for (int i = 0; i < seriesCount; i++) {
 1779                               LegendItem item = renderer.getLegendItem(
 1780                                       datasetIndex, i);
 1781                               if (item != null) {
 1782                                   result.add(item);
 1783                               }
 1784                           }
 1785                       }
 1786                   }
 1787               }
 1788           }
 1789           return result;
 1790       }
 1791   
 1792       /**
 1793        * Handles a 'click' on the plot by updating the anchor value.
 1794        *
 1795        * @param x  x-coordinate of the click (in Java2D space).
 1796        * @param y  y-coordinate of the click (in Java2D space).
 1797        * @param info  information about the plot's dimensions.
 1798        *
 1799        */
 1800       public void handleClick(int x, int y, PlotRenderingInfo info) {
 1801   
 1802           Rectangle2D dataArea = info.getDataArea();
 1803           if (dataArea.contains(x, y)) {
 1804               // set the anchor value for the range axis...
 1805               double java2D = 0.0;
 1806               if (this.orientation == PlotOrientation.HORIZONTAL) {
 1807                   java2D = x;
 1808               }
 1809               else if (this.orientation == PlotOrientation.VERTICAL) {
 1810                   java2D = y;
 1811               }
 1812               RectangleEdge edge = Plot.resolveRangeAxisLocation(
 1813                       getRangeAxisLocation(), this.orientation);
 1814               double value = getRangeAxis().java2DToValue(
 1815                       java2D, info.getDataArea(), edge);
 1816               setAnchorValue(value);
 1817               setRangeCrosshairValue(value);
 1818           }
 1819   
 1820       }
 1821   
 1822       /**
 1823        * Zooms (in or out) on the plot's value axis.
 1824        * <p>
 1825        * If the value 0.0 is passed in as the zoom percent, the auto-range
 1826        * calculation for the axis is restored (which sets the range to include
 1827        * the minimum and maximum data values, thus displaying all the data).
 1828        *
 1829        * @param percent  the zoom amount.
 1830        */
 1831       public void zoom(double percent) {
 1832   
 1833           if (percent > 0.0) {
 1834               double range = getRangeAxis().getRange().getLength();
 1835               double scaledRange = range * percent;
 1836               getRangeAxis().setRange(this.anchorValue - scaledRange / 2.0,
 1837                       this.anchorValue + scaledRange / 2.0);
 1838           }
 1839           else {
 1840               getRangeAxis().setAutoRange(true);
 1841           }
 1842   
 1843       }
 1844   
 1845       /**
 1846        * Receives notification of a change to the plot's dataset.
 1847        * <P>
 1848        * The range axis bounds will be recalculated if necessary.
 1849        *
 1850        * @param event  information about the event (not used here).
 1851        */
 1852       public void datasetChanged(DatasetChangeEvent event) {
 1853   
 1854           int count = this.rangeAxes.size();
 1855           for (int axisIndex = 0; axisIndex < count; axisIndex++) {
 1856               ValueAxis yAxis = getRangeAxis(axisIndex);
 1857               if (yAxis != null) {
 1858                   yAxis.configure();
 1859               }
 1860           }
 1861           if (getParent() != null) {
 1862               getParent().datasetChanged(event);
 1863           }
 1864           else {
 1865               PlotChangeEvent e = new PlotChangeEvent(this);
 1866               e.setType(ChartChangeEventType.DATASET_UPDATED);
 1867               notifyListeners(e);
 1868           }
 1869   
 1870       }
 1871   
 1872       /**
 1873        * Receives notification of a renderer change event.
 1874        *
 1875        * @param event  the event.
 1876        */
 1877       public void rendererChanged(RendererChangeEvent event) {
 1878           Plot parent = getParent();
 1879           if (parent != null) {
 1880               if (parent instanceof RendererChangeListener) {
 1881                   RendererChangeListener rcl = (RendererChangeListener) parent;
 1882                   rcl.rendererChanged(event);
 1883               }
 1884               else {
 1885                   // this should never happen with the existing code, but throw
 1886                   // an exception in case future changes make it possible...
 1887                   throw new RuntimeException(
 1888                       "The renderer has changed and I don't know what to do!");
 1889               }
 1890           }
 1891           else {
 1892               configureRangeAxes();
 1893               PlotChangeEvent e = new PlotChangeEvent(this);
 1894               notifyListeners(e);
 1895           }
 1896       }
 1897   
 1898       /**
 1899        * Adds a marker for display (in the foreground) against the domain axis and
 1900        * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
 1901        * marker will be drawn by the renderer as a line perpendicular to the
 1902        * domain axis, however this is entirely up to the renderer.
 1903        *
 1904        * @param marker  the marker (<code>null</code> not permitted).
 1905        *
 1906        * @see #removeDomainMarker(Marker)
 1907        */
 1908       public void addDomainMarker(CategoryMarker marker) {
 1909           addDomainMarker(marker, Layer.FOREGROUND);
 1910       }
 1911   
 1912       /**
 1913        * Adds a marker for display against the domain axis and sends a
 1914        * {@link PlotChangeEvent} to all registered listeners.  Typically a marker
 1915        * will be drawn by the renderer as a line perpendicular to the domain
 1916        * axis, however this is entirely up to the renderer.
 1917        *
 1918        * @param marker  the marker (<code>null</code> not permitted).
 1919        * @param layer  the layer (foreground or background) (<code>null</code>
 1920        *               not permitted).
 1921        *
 1922        * @see #removeDomainMarker(Marker, Layer)
 1923        */
 1924       public void addDomainMarker(CategoryMarker marker, Layer layer) {
 1925           addDomainMarker(0, marker, layer);
 1926       }
 1927   
 1928       /**
 1929        * Adds a marker for display by a particular renderer and sends a
 1930        * {@link PlotChangeEvent} to all registered listeners.
 1931        * <P>
 1932        * Typically a marker will be drawn by the renderer as a line perpendicular
 1933        * to a domain axis, however this is entirely up to the renderer.
 1934        *
 1935        * @param index  the renderer index.
 1936        * @param marker  the marker (<code>null</code> not permitted).
 1937        * @param layer  the layer (<code>null</code> not permitted).
 1938        *
 1939        * @see #removeDomainMarker(int, Marker, Layer)
 1940        */
 1941       public void addDomainMarker(int index, CategoryMarker marker, Layer layer) {
 1942           addDomainMarker(index, marker, layer, true);
 1943       }
 1944   
 1945       /**
 1946        * Adds a marker for display by a particular renderer and, if requested,
 1947        * sends a {@link PlotChangeEvent} to all registered listeners.
 1948        * <P>
 1949        * Typically a marker will be drawn by the renderer as a line perpendicular
 1950        * to a domain axis, however this is entirely up to the renderer.
 1951        *
 1952        * @param index  the renderer index.
 1953        * @param marker  the marker (<code>null</code> not permitted).
 1954        * @param layer  the layer (<code>null</code> not permitted).
 1955        * @param notify  notify listeners?
 1956        *
 1957        * @since 1.0.10
 1958        *
 1959        * @see #removeDomainMarker(int, Marker, Layer, boolean)
 1960        */
 1961       public void addDomainMarker(int index, CategoryMarker marker, Layer layer,
 1962               boolean notify) {
 1963           if (marker == null) {
 1964               throw new IllegalArgumentException("Null 'marker' not permitted.");
 1965           }
 1966           if (layer == null) {
 1967               throw new IllegalArgumentException("Null 'layer' not permitted.");
 1968           }
 1969           Collection markers;
 1970           if (layer == Layer.FOREGROUND) {
 1971               markers = (Collection) this.foregroundDomainMarkers.get(
 1972                       new Integer(index));
 1973               if (markers == null) {
 1974                   markers = new java.util.ArrayList();
 1975                   this.foregroundDomainMarkers.put(new Integer(index), markers);
 1976               }
 1977               markers.add(marker);
 1978           }
 1979           else if (layer == Layer.BACKGROUND) {
 1980               markers = (Collection) this.backgroundDomainMarkers.get(
 1981                       new Integer(index));
 1982               if (markers == null) {
 1983                   markers = new java.util.ArrayList();
 1984                   this.backgroundDomainMarkers.put(new Integer(index), markers);
 1985               }
 1986               markers.add(marker);
 1987           }
 1988           marker.addChangeListener(this);
 1989           if (notify) {
 1990               fireChangeEvent();
 1991           }
 1992       }
 1993   
 1994       /**
 1995        * Clears all the domain markers for the plot and sends a
 1996        * {@link PlotChangeEvent} to all registered listeners.
 1997        *
 1998        * @see #clearRangeMarkers()
 1999        */
 2000       public void clearDomainMarkers() {
 2001           if (this.backgroundDomainMarkers != null) {
 2002               Set keys = this.backgroundDomainMarkers.keySet();
 2003               Iterator iterator = keys.iterator();
 2004               while (iterator.hasNext()) {
 2005                   Integer key = (Integer) iterator.next();
 2006                   clearDomainMarkers(key.intValue());
 2007               }
 2008               this.backgroundDomainMarkers.clear();
 2009           }
 2010           if (this.foregroundDomainMarkers != null) {
 2011               Set keys = this.foregroundDomainMarkers.keySet();
 2012               Iterator iterator = keys.iterator();
 2013               while (iterator.hasNext()) {
 2014                   Integer key = (Integer) iterator.next();
 2015                   clearDomainMarkers(key.intValue());
 2016               }
 2017               this.foregroundDomainMarkers.clear();
 2018           }
 2019           fireChangeEvent();
 2020       }
 2021   
 2022       /**
 2023        * Returns the list of domain markers (read only) for the specified layer.
 2024        *
 2025        * @param layer  the layer (foreground or background).
 2026        *
 2027        * @return The list of domain markers.
 2028        */
 2029       public Collection getDomainMarkers(Layer layer) {
 2030           return getDomainMarkers(0, layer);
 2031       }
 2032   
 2033       /**
 2034        * Returns a collection of domain markers for a particular renderer and
 2035        * layer.
 2036        *
 2037        * @param index  the renderer index.
 2038        * @param layer  the layer.
 2039        *
 2040        * @return A collection of markers (possibly <code>null</code>).
 2041        */
 2042       public Collection getDomainMarkers(int index, Layer layer) {
 2043           Collection result = null;
 2044           Integer key = new Integer(index);
 2045           if (layer == Layer.FOREGROUND) {
 2046               result = (Collection) this.foregroundDomainMarkers.get(key);
 2047           }
 2048           else if (layer == Layer.BACKGROUND) {
 2049               result = (Collection) this.backgroundDomainMarkers.get(key);
 2050           }
 2051           if (result != null) {
 2052               result = Collections.unmodifiableCollection(result);
 2053           }
 2054           return result;
 2055       }
 2056   
 2057       /**
 2058        * Clears all the domain markers for the specified renderer.
 2059        *
 2060        * @param index  the renderer index.
 2061        *
 2062        * @see #clearRangeMarkers(int)
 2063        */
 2064       public void clearDomainMarkers(int index) {
 2065           Integer key = new Integer(index);
 2066           if (this.backgroundDomainMarkers != null) {
 2067               Collection markers
 2068                   = (Collection) this.backgroundDomainMarkers.get(key);
 2069               if (markers != null) {
 2070                   Iterator iterator = markers.iterator();
 2071                   while (iterator.hasNext()) {
 2072                       Marker m = (Marker) iterator.next();
 2073                       m.removeChangeListener(this);
 2074                   }
 2075                   markers.clear();
 2076               }
 2077           }
 2078           if (this.foregroundDomainMarkers != null) {
 2079               Collection markers
 2080                   = (Collection) this.foregroundDomainMarkers.get(key);
 2081               if (markers != null) {
 2082                   Iterator iterator = markers.iterator();
 2083                   while (iterator.hasNext()) {
 2084                       Marker m = (Marker) iterator.next();
 2085                       m.removeChangeListener(this);
 2086                   }
 2087                   markers.clear();
 2088               }
 2089           }
 2090           fireChangeEvent();
 2091       }
 2092   
 2093       /**
 2094        * Removes a marker for the domain axis and sends a {@link PlotChangeEvent}
 2095        * to all registered listeners.
 2096        *
 2097        * @param marker  the marker.
 2098        *
 2099        * @return A boolean indicating whether or not the marker was actually
 2100        *         removed.
 2101        *
 2102        * @since 1.0.7
 2103        */
 2104       public boolean removeDomainMarker(Marker marker) {
 2105           return removeDomainMarker(marker, Layer.FOREGROUND);
 2106       }
 2107   
 2108       /**
 2109        * Removes a marker for the domain axis in the specified layer and sends a
 2110        * {@link PlotChangeEvent} to all registered listeners.
 2111        *
 2112        * @param marker the marker (<code>null</code> not permitted).
 2113        * @param layer the layer (foreground or background).
 2114        *
 2115        * @return A boolean indicating whether or not the marker was actually
 2116        *         removed.
 2117        *
 2118        * @since 1.0.7
 2119        */
 2120       public boolean removeDomainMarker(Marker marker, Layer layer) {
 2121           return removeDomainMarker(0, marker, layer);
 2122       }
 2123   
 2124       /**
 2125        * Removes a marker for a specific dataset/renderer and sends a
 2126        * {@link PlotChangeEvent} to all registered listeners.
 2127        *
 2128        * @param index the dataset/renderer index.
 2129        * @param marker the marker.
 2130        * @param layer the layer (foreground or background).
 2131        *
 2132        * @return A boolean indicating whether or not the marker was actually
 2133        *         removed.
 2134        *
 2135        * @since 1.0.7
 2136        */
 2137       public boolean removeDomainMarker(int index, Marker marker, Layer layer) {
 2138           return removeDomainMarker(index, marker, layer, true);
 2139       }
 2140   
 2141       /**
 2142        * Removes a marker for a specific dataset/renderer and, if requested,
 2143        * sends a {@link PlotChangeEvent} to all registered listeners.
 2144        *
 2145        * @param index the dataset/renderer index.
 2146        * @param marker the marker.
 2147        * @param layer the layer (foreground or background).
 2148        *
 2149        * @return A boolean indicating whether or not the marker was actually
 2150        *         removed.
 2151        *
 2152        * @since 1.0.10
 2153        */
 2154       public boolean removeDomainMarker(int index, Marker marker, Layer layer,
 2155               boolean notify) {
 2156           ArrayList markers;
 2157           if (layer == Layer.FOREGROUND) {
 2158               markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer(
 2159                       index));
 2160           }
 2161           else {
 2162               markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer(
 2163                       index));
 2164           }
 2165           if (markers == null) {
 2166               return false;
 2167           }
 2168           boolean removed = markers.remove(marker);
 2169           if (removed && notify) {
 2170               fireChangeEvent();
 2171           }
 2172           return removed;
 2173       }
 2174   
 2175       /**
 2176        * Adds a marker for display (in the foreground) against the range axis and
 2177        * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
 2178        * marker will be drawn by the renderer as a line perpendicular to the
 2179        * range axis, however this is entirely up to the renderer.
 2180        *
 2181        * @param marker  the marker (<code>null</code> not permitted).
 2182        *
 2183        * @see #removeRangeMarker(Marker)
 2184        */
 2185       public void addRangeMarker(Marker marker) {
 2186           addRangeMarker(marker, Layer.FOREGROUND);
 2187       }
 2188   
 2189       /**
 2190        * Adds a marker for display against the range axis and sends a
 2191        * {@link PlotChangeEvent} to all registered listeners.  Typically a marker
 2192        * will be drawn by the renderer as a line perpendicular to the range axis,
 2193        * however this is entirely up to the renderer.
 2194        *
 2195        * @param marker  the marker (<code>null</code> not permitted).
 2196        * @param layer  the layer (foreground or background) (<code>null</code>
 2197        *               not permitted).
 2198        *
 2199        * @see #removeRangeMarker(Marker, Layer)
 2200        */
 2201       public void addRangeMarker(Marker marker, Layer layer) {
 2202           addRangeMarker(0, marker, layer);
 2203       }
 2204   
 2205       /**
 2206        * Adds a marker for display by a particular renderer and sends a
 2207        * {@link PlotChangeEvent} to all registered listeners.
 2208        * <P>
 2209        * Typically a marker will be drawn by the renderer as a line perpendicular
 2210        * to a range axis, however this is entirely up to the renderer.
 2211        *
 2212        * @param index  the renderer index.
 2213        * @param marker  the marker.
 2214        * @param layer  the layer.
 2215        *
 2216        * @see #removeRangeMarker(int, Marker, Layer)
 2217        */
 2218       public void addRangeMarker(int index, Marker marker, Layer layer) {
 2219           addRangeMarker(index, marker, layer, true);
 2220       }
 2221   
 2222       /**
 2223        * Adds a marker for display by a particular renderer and sends a
 2224        * {@link PlotChangeEvent} to all registered listeners.
 2225        * <P>
 2226        * Typically a marker will be drawn by the renderer as a line perpendicular
 2227        * to a range axis, however this is entirely up to the renderer.
 2228        *
 2229        * @param index  the renderer index.
 2230        * @param marker  the marker.
 2231        * @param layer  the layer.
 2232        * @param notify  notify listeners?
 2233        *
 2234        * @since 1.0.10
 2235        *
 2236        * @see #removeRangeMarker(int, Marker, Layer, boolean)
 2237        */
 2238       public void addRangeMarker(int index, Marker marker, Layer layer,
 2239               boolean notify) {
 2240           Collection markers;
 2241           if (layer == Layer.FOREGROUND) {
 2242               markers = (Collection) this.foregroundRangeMarkers.get(
 2243                       new Integer(index));
 2244               if (markers == null) {
 2245                   markers = new java.util.ArrayList();
 2246                   this.foregroundRangeMarkers.put(new Integer(index), markers);
 2247               }
 2248               markers.add(marker);
 2249           }
 2250           else if (layer == Layer.BACKGROUND) {
 2251               markers = (Collection) this.backgroundRangeMarkers.get(
 2252                       new Integer(index));
 2253               if (markers == null) {
 2254                   markers = new java.util.ArrayList();
 2255                   this.backgroundRangeMarkers.put(new Integer(index), markers);
 2256               }
 2257               markers.add(marker);
 2258           }
 2259           marker.addChangeListener(this);
 2260           if (notify) {
 2261               fireChangeEvent();
 2262           }
 2263       }
 2264   
 2265       /**
 2266        * Clears all the range markers for the plot and sends a
 2267        * {@link PlotChangeEvent} to all registered listeners.
 2268        *
 2269        * @see #clearDomainMarkers()
 2270        */
 2271       public void clearRangeMarkers() {
 2272           if (this.backgroundRangeMarkers != null) {
 2273               Set keys = this.backgroundRangeMarkers.keySet();
 2274               Iterator iterator = keys.iterator();
 2275               while (iterator.hasNext()) {
 2276                   Integer key = (Integer) iterator.next();
 2277                   clearRangeMarkers(key.intValue());
 2278               }
 2279               this.backgroundRangeMarkers.clear();
 2280           }
 2281           if (this.foregroundRangeMarkers != null) {
 2282               Set keys = this.foregroundRangeMarkers.keySet();
 2283               Iterator iterator = keys.iterator();
 2284               while (iterator.hasNext()) {
 2285                   Integer key = (Integer) iterator.next();
 2286                   clearRangeMarkers(key.intValue());
 2287               }
 2288               this.foregroundRangeMarkers.clear();
 2289           }
 2290           fireChangeEvent();
 2291       }
 2292   
 2293       /**
 2294        * Returns the list of range markers (read only) for the specified layer.
 2295        *
 2296        * @param layer  the layer (foreground or background).
 2297        *
 2298        * @return The list of range markers.
 2299        *
 2300        * @see #getRangeMarkers(int, Layer)
 2301        */
 2302       public Collection getRangeMarkers(Layer layer) {
 2303           return getRangeMarkers(0, layer);
 2304       }
 2305   
 2306       /**
 2307        * Returns a collection of range markers for a particular renderer and
 2308        * layer.
 2309        *
 2310        * @param index  the renderer index.
 2311        * @param layer  the layer.
 2312        *
 2313        * @return A collection of markers (possibly <code>null</code>).
 2314        */
 2315       public Collection getRangeMarkers(int index, Layer layer) {
 2316           Collection result = null;
 2317           Integer key = new Integer(index);
 2318           if (layer == Layer.FOREGROUND) {
 2319               result = (Collection) this.foregroundRangeMarkers.get(key);
 2320           }
 2321           else if (layer == Layer.BACKGROUND) {
 2322               result = (Collection) this.backgroundRangeMarkers.get(key);
 2323           }
 2324           if (result != null) {
 2325               result = Collections.unmodifiableCollection(result);
 2326           }
 2327           return result;
 2328       }
 2329   
 2330       /**
 2331        * Clears all the range markers for the specified renderer.
 2332        *
 2333        * @param index  the renderer index.
 2334        *
 2335        * @see #clearDomainMarkers(int)
 2336        */
 2337       public void clearRangeMarkers(int index) {
 2338           Integer key = new Integer(index);
 2339           if (this.backgroundRangeMarkers != null) {
 2340               Collection markers
 2341                   = (Collection) this.backgroundRangeMarkers.get(key);
 2342               if (markers != null) {
 2343                   Iterator iterator = markers.iterator();
 2344                   while (iterator.hasNext()) {
 2345                       Marker m = (Marker) iterator.next();
 2346                       m.removeChangeListener(this);
 2347                   }
 2348                   markers.clear();
 2349               }
 2350           }
 2351           if (this.foregroundRangeMarkers != null) {
 2352               Collection markers
 2353                   = (Collection) this.foregroundRangeMarkers.get(key);
 2354               if (markers != null) {
 2355                   Iterator iterator = markers.iterator();
 2356                   while (iterator.hasNext()) {
 2357                       Marker m = (Marker) iterator.next();
 2358                       m.removeChangeListener(this);
 2359                   }
 2360                   markers.clear();
 2361               }
 2362           }
 2363           fireChangeEvent();
 2364       }
 2365   
 2366       /**
 2367        * Removes a marker for the range axis and sends a {@link PlotChangeEvent}
 2368        * to all registered listeners.
 2369        *
 2370        * @param marker the marker.
 2371        *
 2372        * @return A boolean indicating whether or not the marker was actually
 2373        *         removed.
 2374        *
 2375        * @since 1.0.7
 2376        *
 2377        * @see #addRangeMarker(Marker)
 2378        */
 2379       public boolean removeRangeMarker(Marker marker) {
 2380           return removeRangeMarker(marker, Layer.FOREGROUND);
 2381       }
 2382   
 2383       /**
 2384        * Removes a marker for the range axis in the specified layer and sends a
 2385        * {@link PlotChangeEvent} to all registered listeners.
 2386        *
 2387        * @param marker the marker (<code>null</code> not permitted).
 2388        * @param layer the layer (foreground or background).
 2389        *
 2390        * @return A boolean indicating whether or not the marker was actually
 2391        *         removed.
 2392        *
 2393        * @since 1.0.7
 2394        *
 2395        * @see #addRangeMarker(Marker, Layer)
 2396        */
 2397       public boolean removeRangeMarker(Marker marker, Layer layer) {
 2398           return removeRangeMarker(0, marker, layer);
 2399       }
 2400   
 2401       /**
 2402        * Removes a marker for a specific dataset/renderer and sends a
 2403        * {@link PlotChangeEvent} to all registered listeners.
 2404        *
 2405        * @param index the dataset/renderer index.
 2406        * @param marker the marker.
 2407        * @param layer the layer (foreground or background).
 2408        *
 2409        * @return A boolean indicating whether or not the marker was actually
 2410        *         removed.
 2411        *
 2412        * @since 1.0.7
 2413        *
 2414        * @see #addRangeMarker(int, Marker, Layer)
 2415        */
 2416       public boolean removeRangeMarker(int index, Marker marker, Layer layer) {
 2417           return removeRangeMarker(index, marker, layer, true);
 2418       }
 2419   
 2420       /**
 2421        * Removes a marker for a specific dataset/renderer and sends a
 2422        * {@link PlotChangeEvent} to all registered listeners.
 2423        *
 2424        * @param index  the dataset/renderer index.
 2425        * @param marker  the marker.
 2426        * @param layer  the layer (foreground or background).
 2427        * @param notify  notify listeners.
 2428        *
 2429        * @return A boolean indicating whether or not the marker was actually
 2430        *         removed.
 2431        *
 2432        * @since 1.0.10
 2433        *
 2434        * @see #addRangeMarker(int, Marker, Layer, boolean)
 2435        */
 2436       public boolean removeRangeMarker(int index, Marker marker, Layer layer,
 2437               boolean notify) {
 2438           if (marker == null) {
 2439               throw new IllegalArgumentException("Null 'marker' argument.");
 2440           }
 2441           ArrayList markers;
 2442           if (layer == Layer.FOREGROUND) {
 2443               markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer(
 2444                       index));
 2445           }
 2446           else {
 2447               markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer(
 2448                       index));
 2449           }
 2450           if (markers == null) {
 2451               return false;
 2452           }
 2453           boolean removed = markers.remove(marker);
 2454           if (removed && notify) {
 2455               fireChangeEvent();
 2456           }
 2457           return removed;
 2458       }
 2459   
 2460       /**
 2461        * Returns a flag indicating whether or not the range crosshair is visible.
 2462        *
 2463        * @return The flag.
 2464        *
 2465        * @see #setRangeCrosshairVisible(boolean)
 2466        */
 2467       public boolean isRangeCrosshairVisible() {
 2468           return this.rangeCrosshairVisible;
 2469       }
 2470   
 2471       /**
 2472        * Sets the flag indicating whether or not the range crosshair is visible.
 2473        *
 2474        * @param flag  the new value of the flag.
 2475        *
 2476        * @see #isRangeCrosshairVisible()
 2477        */
 2478       public void setRangeCrosshairVisible(boolean flag) {
 2479           if (this.rangeCrosshairVisible != flag) {
 2480               this.rangeCrosshairVisible = flag;
 2481               fireChangeEvent();
 2482           }
 2483       }
 2484   
 2485       /**
 2486        * Returns a flag indicating whether or not the crosshair should "lock-on"
 2487        * to actual data values.
 2488        *
 2489        * @return The flag.
 2490        *
 2491        * @see #setRangeCrosshairLockedOnData(boolean)
 2492        */
 2493       public boolean isRangeCrosshairLockedOnData() {
 2494           return this.rangeCrosshairLockedOnData;
 2495       }
 2496   
 2497       /**
 2498        * Sets the flag indicating whether or not the range crosshair should
 2499        * "lock-on" to actual data values, and sends a {@link PlotChangeEvent}
 2500        * to all registered listeners.
 2501        *
 2502        * @param flag  the flag.
 2503        *
 2504        * @see #isRangeCrosshairLockedOnData()
 2505        */
 2506       public void setRangeCrosshairLockedOnData(boolean flag) {
 2507           if (this.rangeCrosshairLockedOnData != flag) {
 2508               this.rangeCrosshairLockedOnData = flag;
 2509               fireChangeEvent();
 2510           }
 2511       }
 2512   
 2513       /**
 2514        * Returns the range crosshair value.
 2515        *
 2516        * @return The value.
 2517        *
 2518        * @see #setRangeCrosshairValue(double)
 2519        */
 2520       public double getRangeCrosshairValue() {
 2521           return this.rangeCrosshairValue;
 2522       }
 2523   
 2524       /**
 2525        * Sets the range crosshair value and, if the crosshair is visible, sends
 2526        * a {@link PlotChangeEvent} to all registered listeners.
 2527        *
 2528        * @param value  the new value.
 2529        *
 2530        * @see #getRangeCrosshairValue()
 2531        */
 2532       public void setRangeCrosshairValue(double value) {
 2533           setRangeCrosshairValue(value, true);
 2534       }
 2535   
 2