/* ===========================================================
 
* JFreeChart : a free chart library for the Java(tm) platform
 
* ===========================================================
 
*
 
* (C) Copyright 2000-2017, by Object Refinery Limited and Contributors.
 
*
 
* Project Info:
  
http://www.jfree.org/jfreechart/index.html
 
*
 
* This library is free software; you can redistribute it and/or modify it
 
* under the terms of the GNU Lesser General Public License as published by
 
* the Free Software Foundation; either version 2.1 of the License, or
 
* (at your option) any later version.
 
*
 
* This library is distributed in the hope that it will be useful, but
 
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 
* License for more details.
 
*
 
* You should have received a copy of the GNU Lesser General Public
 
* License along with this library; if not, write to the Free Software
 
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  
02110-1301,
 
* USA.
 
*
 
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
 
* Other names may be trademarks of their respective owners.]
 
*
 
* -----------------
 
* CategoryPlot.java
 
* -----------------
 
* (C) Copyright 2000-2017, by Object Refinery Limited and Contributors.
 
*
 
* Original Author:
  
David Gilbert (for Object Refinery Limited);
 
* Contributor(s):
   
Jeremy Bowman;
 
*
                   
Arnaud Lelievre;
 
*
                   
Richard West, Advanced Micro Devices, Inc.;
 
*
                   
Ulrich Voigt - patch 2686040;
 
*
                   
Peter Kolb - patches 2603321 and 2809117;
 
*
 
* Changes
 
* -------
 
* 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
 
* 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
 
* 18-Sep-2001 : Updated header (DG);
 
* 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
 
* 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
 
* 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
 
*
               
available space rather than a fixed number of units (DG);
 
* 12-Dec-2001 : Changed constructors to protected (DG);
 
* 13-Dec-2001 : Added tooltips (DG);
 
* 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
 
*
               
some argument checking code.
  
Thanks to Taoufik Romdhane for
 
*
               
suggesting this (DG);
 
* 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
 
*
               
alpha-transparency for Plot and subclasses (DG);
 
* 06-Mar-2002 : Updated import statements (DG);
 
* 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
 
*
               
to use the CategoryItemRenderer interface (DG);
 
* 22-Mar-2002 : Dropped the getCategories() method (DG);
 
* 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
 
*
               
class (DG);
 
* 29-Apr-2002 : New methods to support printing values at the end of bars,
 
*
               
contributed by Jeremy Bowman (DG);
 
* 11-May-2002 : New methods for label visibility and overlaid plot support,
 
*
               
contributed by Jeremy Bowman (DG);
 
* 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
 
*
               
renderer.
  
Moved constants into the CategoryPlotConstants
 
*
               
interface.
  
Updated Javadoc comments (DG);
 
* 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
 
*
               
lower bound on the range axis (if necessary), updated
 
*
               
Javadocs (DG);
 
* 25-Jun-2002 : Removed redundant imports (DG);
 
* 20-Aug-2002 : Changed the constructor for Marker (DG);
 
* 28-Aug-2002 : Added listener notification to setDomainAxis() and
 
*
               
setRangeAxis() (DG);
 
* 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
 
*
               
Checkstyle (DG);
 
* 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
 
* 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
 
* 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
 
* 18-Nov-2002 : Added grid settings for both domain and range axis (previously
 
*
               
these were set in the axes) (DG);
 
* 19-Nov-2002 : Added axis location parameters to constructor (DG);
 
* 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
 
* 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
 
* 26-Mar-2003 : Implemented Serializable (DG);
 
* 02-May-2003 : Moved render() method up from subclasses. Added secondary
 
*
               
range markers. Added an attribute to control the dataset
 
*
               
rendering order.
  
Added a drawAnnotations() method.Changed
 
*
               
the axis location from an int to an AxisLocation (DG);
 
* 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
 
*
               
this class (DG);
 
* 02-Jun-2003 : Removed check for range axis compatibility (DG);
 
* 04-Jul-2003 : Added a domain gridline position attribute (DG);
 
* 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
 
* 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
 
* 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
 
*
               
changes) (DG);
 
* 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
 
*
               
790407 (initialise method) (DG);
 
* 08-Sep-2003 : Added internationalization via use of properties
 
*
               
resourceBundle (RFE 690236) (AL);
 
* 08-Sep-2003 : Fixed bug (wrong secondary range axis being used).
  
Changed
 
*
               
ValueAxis API (DG);
 
* 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
 
* 15-Sep-2003 : Fixed two bugs in serialization, implemented
 
*
               
PublicCloneable (DG);
 
* 23-Oct-2003 : Added event notification for changes to renderer (DG);
 
* 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
 
* 03-Dec-2003 : Modified draw method to accept anchor (DG);
 
* 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
 
* 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
 
*
               
stacked (DG);
 
* 12-May-2004 : Added fixed legend items (DG);
 
* 19-May-2004 : Added check for null legend item from renderer (DG);
 
* 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
 
* 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
 
*
               
--> datasetsMappedToRangeAxis(), and ensured that returned
 
*
               
list doesn't contain null datasets (DG);
 
* 12-Nov-2004 : Implemented new Zoomable interface (DG);
 
* 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
 
*
               
CategoryItemRenderer (DG);
 
* 04-May-2005 : Fixed serialization of range markers (DG);
 
* 05-May-2005 : Updated draw() method parameters (DG);
 
* 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
 
*
               
RFE 1183100 (DG);
 
* 01-Jun-2005 : Upon deserialization, register plot as a listener with its
 
*
               
axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
 
* 02-Jun-2005 : Added support for domain markers (DG);
 
* 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG);
 
* 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
 
* 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to
 
*
               
match XYPlot (see RFE 1220495) (DG);
 
* ------------- JFREECHART 1.0.x ---------------------------------------------
 
* 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the
 
*
               
renderer might influence the axis range (DG);
 
* 27-Jan-2006 : Added various null argument checks (DG);
 
* 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing
 
*
               
category labels, thanks to Adriaan Joubert (1277726) (DG);
 
* 05-Sep-2006 : Added MarkerChangeEvent support (DG);
 
* 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and
 
*
               
getCategoriesForAxis() methods (DG);
 
* 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and
 
*
               
setRowRenderingOrder() (DG);
 
* 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data
 
*
               
area) (DG);
 
* 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument
 
*
               
ignored) (DG);
 
* 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and
 
*
               
setRangeCrosshairStroke(), fixed clipping for
 
*
               
annotations (DG);
 
* 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG);
 
* 10-Jul-2007 : Added getRangeAxisIndex(ValueAxis) method (DG);
 
* 24-Sep-2007 : Implemented new zoom methods (DG);
 
* 25-Oct-2007 : Added some argument checks (DG);
 
* 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
 
*
               
and range markers (DG);
 
* 14-Nov-2007 : Added missing event notifications (DG);
 
* 25-Mar-2008 : Added new methods with optional notification - see patch
 
*
               
1913751 (DG);
 
* 07-Apr-2008 : Fixed NPE in removeDomainMarker() and
 
*
               
removeRangeMarker() (DG);
 
* 23-Apr-2008 : Fixed equals() and clone() methods (DG);
 
* 26-Jun-2008 : Fixed crosshair support (DG);
 
* 10-Jul-2008 : Fixed outline visibility for 3D renderers (DG);
 
* 12-Aug-2008 : Added rendererCount() method (DG);
 
* 25-Nov-2008 : Added facility to map datasets to multiples axes (DG);
 
* 15-Dec-2008 : Cleaned up grid drawing methods (DG);
 
* 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by
 
*
               
Jess Thrysoee (DG);
 
* 21-Jan-2009 : Added rangeMinorGridlinesVisible flag (DG);
 
* 18-Mar-2009 : Modified anchored zoom behaviour (DG);
 
* 19-Mar-2009 : Implemented Pannable interface - see patch 2686040 (DG);
 
* 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG);
 
* 24-Jun-2009 : Implemented AnnotationChangeListener (see patch 2809117 by
 
*
               
PK) (DG);
 
* 06-Jul-2009 : Fix for cloning of renderers - see bug 2817504 (DG)
 
* 10-Jul-2009 : Added optional drop shadow generator (DG);
 
* 27-Sep-2011 : Fixed annotation import (DG);
 
* 18-Oct-2011 : Fixed tooltip offset with shadow generator (DG);
 
* 20-Nov-2011 : Initialise shadow generator as null (DG);
 
* 02-Jul-2013 : Use ParamChecks (DG);
 
* 12-Sep-2013 : Check for KEY_SUPPRESS_SHADOW_GENERATION rendering hint (DG);
 
* 10-Mar-2014 : Updated Javadocs for issue #1123 (DG);
 
* 09-Apr-2014 : Remove use of ObjectList (DG);
 
*
 
*/

package org.jfree.chart.plot;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.annotations.Annotation;
import org.jfree.chart.annotations.CategoryAnnotation;
import org.jfree.chart.axis.Axis;
import org.jfree.chart.axis.AxisCollection;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.AxisSpace;
import org.jfree.chart.axis.AxisState;
import org.jfree.chart.axis.CategoryAnchor;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.TickType;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.axis.ValueTick;
import org.jfree.chart.event.AnnotationChangeEvent;
import org.jfree.chart.event.AnnotationChangeListener;
import org.jfree.chart.event.ChartChangeEventType;
import org.jfree.chart.event.PlotChangeEvent;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.event.RendererChangeListener;
import org.jfree.chart.renderer.category.AbstractCategoryItemRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.ui.Layer;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.chart.util.CloneUtils;
import org.jfree.chart.util.ObjectUtils;
import org.jfree.chart.util.PaintUtils;
import org.jfree.chart.util.Args;
import org.jfree.chart.util.PublicCloneable;
import org.jfree.chart.util.ResourceBundleWrapper;
import org.jfree.chart.util.SerialUtils;
import org.jfree.chart.util.ShadowGenerator;
import org.jfree.chart.util.ShapeUtils;
import org.jfree.chart.util.SortOrder;
import org.jfree.data.Range;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetChangeEvent;
import org.jfree.data.general.DatasetUtils;

/**
 
* A general plotting class that uses data from a {@link CategoryDataset} and
 
* renders each data item using a {@link CategoryItemRenderer}.
 
*/

public class CategoryPlot extends Plot implements ValueAxisPlot, Pannable,
        
Zoomable, AnnotationChangeListener, RendererChangeListener,
        
Cloneable, PublicCloneable, Serializable {

    
/** For serialization. */
    
private static final long serialVersionUID = -3537691700434728188L;

    
/**
     
* The default visibility of the grid lines plotted against the domain
     
* axis.
     
*/

    
public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;

    
/**
     
* The default visibility of the grid lines plotted against the range
     
* axis.
     
*/

    
public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;

    
/** The default grid line stroke. */
    
public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
            
BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[]
            
{2.0f, 2.0f}, 0.0f);

    
/** The default grid line paint. */
    
public static final Paint DEFAULT_GRIDLINE_PAINT = Color.LIGHT_GRAY;

    
/** The default value label font. */
    
public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif",
            
Font.PLAIN, 10);

    
/**
     
* The default crosshair visibility.
     
*
     
* @since 1.0.5
     
*/

    
public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;

    
/**
     
* The default crosshair stroke.
     
*
     
* @since 1.0.5
     
*/

    
public static final Stroke DEFAULT_CROSSHAIR_STROKE
            
= DEFAULT_GRIDLINE_STROKE;

    
/**
     
* The default crosshair paint.
     
*
     
* @since 1.0.5
     
*/

    
public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.BLUE;

    
/** The resourceBundle for the localization. */
    
protected static ResourceBundle localizationResources
            
= ResourceBundleWrapper.getBundle(
            
"org.jfree.chart.plot.LocalizationBundle");

    
/** The plot orientation. */
    
private PlotOrientation orientation;

    
/** The offset between the data area and the axes. */
    
private RectangleInsets axisOffset;

    
/** Storage for the domain axes. */
    
private Map<Integer, CategoryAxis> domainAxes;

    
/** Storage for the domain axis locations. */
    
private Map<Integer, AxisLocation> domainAxisLocations;

    
/**
     
* A flag that controls whether or not the shared domain axis is drawn
     
* (only relevant when the plot is being used as a subplot).
     
*/

    
private boolean drawSharedDomainAxis;

    
/** Storage for the range axes. */
    
private Map<Integer, ValueAxis> rangeAxes;

    
/** Storage for the range axis locations. */
    
private Map<Integer, AxisLocation> rangeAxisLocations;

    
/** Storage for the datasets. */
    
private Map<Integer, CategoryDataset> datasets;

    
/**
 

     
* Storage for keys that map each dataset to one or more domain axes.
     
* Typically a dataset is rendered using the scale of a single axis, but
     
* a dataset can contribute to the "auto-range" of any number of axes.
     
*/

    
private TreeMap<Integer, List<Integer>> datasetToDomainAxesMap;

    
/**
 

     
* Storage for keys that map each dataset to one or more range axes.
 

     
* Typically a dataset is rendered using the scale of a single axis, but
     
* a dataset can contribute to the "auto-range" of any number of axes.
     
*/

    
private TreeMap<Integer, List<Integer>> datasetToRangeAxesMap;

    
/** Storage for the renderers. */
    
private Map<Integer, CategoryItemRenderer> renderers;

    
/** The dataset rendering order. */
    
private DatasetRenderingOrder renderingOrder
            
= DatasetRenderingOrder.REVERSE;

    
/**
     
* Controls the order in which the columns are traversed when rendering the
     
* data items.
     
*/

    
private SortOrder columnRenderingOrder = SortOrder.ASCENDING;

    
/**
     
* Controls the order in which the rows are traversed when rendering the
     
* data items.
     
*/

    
private SortOrder rowRenderingOrder = SortOrder.ASCENDING;

    
/**
     
* A flag that controls whether the grid-lines for the domain axis are
     
* visible.
     
*/

    
private boolean domainGridlinesVisible;

    
/** The position of the domain gridlines relative to the category. */
    
private CategoryAnchor domainGridlinePosition;

    
/** The stroke used to draw the domain grid-lines. */
    
private transient Stroke domainGridlineStroke;

    
/** The paint used to draw the domain
  
grid-lines. */
    
private transient Paint domainGridlinePaint;

    
/**
     
* A flag that controls whether or not the zero baseline against the range
     
* axis is visible.
     
*
     
* @since 1.0.13
     
*/

    
private boolean rangeZeroBaselineVisible;

    
/**
     
* The stroke used for the zero baseline against the range axis.
     
*
     
* @since 1.0.13
     
*/

    
private transient Stroke rangeZeroBaselineStroke;

    
/**
     
* The paint used for the zero baseline against the range axis.
     
*
     
* @since 1.0.13
     
*/

    
private transient Paint rangeZeroBaselinePaint;

    
/**
     
* A flag that controls whether the grid-lines for the range axis are
     
* visible.
     
*/

    
private boolean rangeGridlinesVisible;

    
/** The stroke used to draw the range axis grid-lines. */
    
private transient Stroke rangeGridlineStroke;

    
/** The paint used to draw the range axis grid-lines. */
    
private transient Paint rangeGridlinePaint;

    
/**
     
* A flag that controls whether or not gridlines are shown for the minor
     
* tick values on the primary range axis.
     
*
     
* @since 1.0.13
     
*/

    
private boolean rangeMinorGridlinesVisible;

    
/**
     
* The stroke used to draw the range minor grid-lines.
     
*
     
* @since 1.0.13
     
*/

    
private transient Stroke rangeMinorGridlineStroke;

    
/**
     
* The paint used to draw the range minor grid-lines.
     
*
     
* @since 1.0.13
     
*/

    
private transient Paint rangeMinorGridlinePaint;

    
/** The anchor value. */
    
private double anchorValue;

    
/**
     
* The index for the dataset that the crosshairs are linked to (this
     
* determines which axes the crosshairs are plotted against).
     
*
     
* @since 1.0.11
     
*/

    
private int crosshairDatasetIndex;

    
/**
     
* A flag that controls the visibility of the domain crosshair.
     
*
     
* @since 1.0.11
     
*/

    
private boolean domainCrosshairVisible;

    
/**
     
* The row key for the crosshair point.
     
*
     
* @since 1.0.11
     
*/
    
private Comparable domainCrosshairRowKey;

    
/**
     
* The column key for the crosshair point.
     
*
     
* @since 1.0.11
     
*/

    
private Comparable domainCrosshairColumnKey;

    
/**
     
* The stroke used to draw the domain crosshair if it is visible.
     
*
     
* @since 1.0.11
     
*/

    
private transient Stroke domainCrosshairStroke;

    
/**
     
* The paint used to draw the domain crosshair if it is visible.
     
*
     
* @since 1.0.11
     
*/

    
private transient Paint domainCrosshairPaint;

    
/** A flag that controls whether or not a range crosshair is drawn. */
    
private boolean rangeCrosshairVisible;

    
/** The range crosshair value. */
    
private double rangeCrosshairValue;

    
/** The pen/brush used to draw the crosshair (if any). */
    
private transient Stroke rangeCrosshairStroke;

    
/** The color used to draw the crosshair (if any). */
    
private transient Paint rangeCrosshairPaint;

    
/**
     
* A flag that controls whether or not the crosshair locks onto actual
     
* data points.
     
*/

    
private boolean rangeCrosshairLockedOnData = true;

    
/** A map containing lists of markers for the domain axes. */
    
private Map foregroundDomainMarkers;

    
/** A map containing lists of markers for the domain axes. */
    
private Map backgroundDomainMarkers;

    
/** A map containing lists of markers for the range axes. */
    
private Map foregroundRangeMarkers;

    
/** A map containing lists of markers for the range axes. */
    
private Map backgroundRangeMarkers;

    
/**
     
* A (possibly empty) list of annotations for the plot.
  
The list should
     
* be initialised in the constructor and never allowed to be
     
* {@code null}.
     
*/

    
private List annotations;

    
/**
     
* The weight for the plot (only relevant when the plot is used as a subplot
     
* within a combined plot).
     
*/

    
private int weight;

    
/** The fixed space for the domain axis. */
    
private AxisSpace fixedDomainAxisSpace;

    
/** The fixed space for the range axis. */
    
private AxisSpace fixedRangeAxisSpace;

    
/**
     
* An optional collection of legend items that can be returned by the
     
* getLegendItems() method.
     
*/

    
private LegendItemCollection fixedLegendItems;

    
/**
     
* A flag that controls whether or not panning is enabled for the
     
* range axis/axes.
     
*
     
* @since 1.0.13
     
*/

    
private boolean rangePannable;

    
/**
     
* The shadow generator for the plot ({@code null} permitted).
     
*
     
* @since 1.0.14
     
*/

    
private ShadowGenerator shadowGenerator;

    
/**
     
* Default constructor.
     
*/
    
public CategoryPlot() {
        
this(null, null, null, null);
    
}

    
/**
     
* Creates a new plot.
     
*
     
* @param dataset
  
the dataset ({@code null} permitted).
     
* @param domainAxis
  
the domain axis ({@code null} permitted).
     
* @param rangeAxis
  
the range axis ({@code null} permitted).
     
* @param renderer
  
the item renderer ({@code null} permitted).
     
*
     
*/

    
public CategoryPlot(CategoryDataset dataset, CategoryAxis domainAxis,
            
ValueAxis rangeAxis, CategoryItemRenderer renderer) {

        
super();

        
this.orientation = PlotOrientation.VERTICAL;

        
// allocate storage for dataset, axes and renderers
        
this.domainAxes = new HashMap<Integer, CategoryAxis>();
        
this.domainAxisLocations = new HashMap<Integer, AxisLocation>();
        
this.rangeAxes = new HashMap<Integer, ValueAxis>();
        
this.rangeAxisLocations = new HashMap<Integer, AxisLocation>();

        
this.datasetToDomainAxesMap = new TreeMap();
        
this.datasetToRangeAxesMap = new TreeMap();

        
this.renderers = new HashMap<Integer, CategoryItemRenderer>();

        
this.datasets = new HashMap<Integer, CategoryDataset>();
        
this.datasets.put(0, dataset);
        
if (dataset != null) {
            
dataset.addChangeListener(this);
        
}

        
this.axisOffset = RectangleInsets.ZERO_INSETS;
        
this.domainAxisLocations.put(0, AxisLocation.BOTTOM_OR_LEFT);
        
this.rangeAxisLocations.put(0, AxisLocation.TOP_OR_LEFT);

        
this.renderers.put(0, renderer);
        
if (renderer != null) {
            
renderer.setPlot(this);
            
renderer.addChangeListener(this);
        
}

        
this.domainAxes.put(0, domainAxis);
        
mapDatasetToDomainAxis(0, 0);
        
if (domainAxis != null) {
            
domainAxis.setPlot(this);
            
domainAxis.addChangeListener(this);
        
}
        
this.drawSharedDomainAxis = false;

        
this.rangeAxes.put(0, rangeAxis);
        
mapDatasetToRangeAxis(0, 0);
        
if (rangeAxis != null) {
            
rangeAxis.setPlot(this);
            
rangeAxis.addChangeListener(this);
        
}

        
configureDomainAxes();
        
configureRangeAxes();

        
this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
        
this.domainGridlinePosition = CategoryAnchor.MIDDLE;
        
this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
        
this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;

        
this.rangeZeroBaselineVisible = false;
        
this.rangeZeroBaselinePaint = Color.BLACK;
        
this.rangeZeroBaselineStroke = new BasicStroke(0.5f);

        
this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
        
this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
        
this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;

        
this.rangeMinorGridlinesVisible = false;
        
this.rangeMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE;
        
this.rangeMinorGridlinePaint = Color.WHITE;

        
this.foregroundDomainMarkers = new HashMap();
        
this.backgroundDomainMarkers = new HashMap();
        
this.foregroundRangeMarkers = new HashMap();
        
this.backgroundRangeMarkers = new HashMap();

        
this.anchorValue = 0.0;

        
this.domainCrosshairVisible = false;
        
this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
        
this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;

        
this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE;
        
this.rangeCrosshairValue = 0.0;
        
this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
        
this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;

        
this.annotations = new java.util.ArrayList();

        
this.rangePannable = false;
        
this.shadowGenerator = null;
    
}

    
/**
     
* Returns a string describing the type of plot.
     
*
     
* @return The type.
     
*/

    
@Override
    
public String getPlotType() {
        
return localizationResources.getString("Category_Plot");
    
}

    
/**
     
* Returns the orientation of the plot.
     
*
     
* @return The orientation of the plot (never {@code null}).
     
*
     
* @see #setOrientation(PlotOrientation)
     
*/

    
@Override
    
public PlotOrientation getOrientation() {
        
return this.orientation;
    
}

    
/**
     
* Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param orientation
  
the orientation ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setOrientation(PlotOrientation orientation) {
        
Args.nullNotPermitted(orientation, "orientation");
        
this.orientation = orientation;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the axis offset.
     
*
     
* @return The axis offset (never {@code null}).
     
*
     
* @see #setAxisOffset(RectangleInsets)
     
*/

    
public RectangleInsets getAxisOffset() {
        
return this.axisOffset;
    
}

    
/**
     
* Sets the axis offsets (gap between the data area and the axes) and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param offset
  
the offset ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setAxisOffset(RectangleInsets offset) {
        
Args.nullNotPermitted(offset, "offset");
        
this.axisOffset = offset;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the domain axis for the plot.
  
If the domain axis for this plot
     
* is {@code null}, then the method will return the parent plot's
     
* domain axis (if there is a parent plot).
     
*
     
* @return The domain axis ({@code null} permitted).
     
*
     
* @see #setDomainAxis(CategoryAxis)
     
*/

    
public CategoryAxis getDomainAxis() {
        
return getDomainAxis(0);
    
}

    
/**
     
* Returns a domain axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The axis ({@code null} possible).
     
*
     
* @see #setDomainAxis(int, CategoryAxis)
     
*/

    
public CategoryAxis getDomainAxis(int index) {
        
CategoryAxis result = (CategoryAxis) this.domainAxes.get(index);
        
if (result == null) {
            
Plot parent = getParent();
            
if (parent instanceof CategoryPlot) {
                
CategoryPlot cp = (CategoryPlot) parent;
                
result = cp.getDomainAxis(index);
            
}
        
}
        
return result;
    
}

    
/**
     
* Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param axis
  
the axis ({@code null} permitted).
     
*
     
*
 

     
*/

    
public void setDomainAxis(CategoryAxis axis) {
        
setDomainAxis(0, axis);
    
}

    
/**
     
* Sets a domain axis and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param axis
  
the axis ({@code null} permitted).
     
*
     
* @see #getDomainAxis(int)
     
*/

    
public void setDomainAxis(int index, CategoryAxis axis) {
        
setDomainAxis(index, axis, true);
    
}

    
/**
     
* Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param axis
  
the axis ({@code null} permitted).
     
* @param notify
  
notify listeners?
     
*/

    
public void setDomainAxis(int index, CategoryAxis axis, boolean notify) {
        
CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
        
if (existing != null) {
            
existing.removeChangeListener(this);
        
}
        
if (axis != null) {
            
axis.setPlot(this);
        
}
        
this.domainAxes.put(index, axis);
        
if (axis != null) {
            
axis.configure();
            
axis.addChangeListener(this);
        
}
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param axes
  
the axes ({@code null} not permitted).
     
*
     
* @see #setRangeAxes(ValueAxis[])
     
*/

    
public void setDomainAxes(CategoryAxis[] axes) {
        
for (int i = 0; i < axes.length; i++) {
            
setDomainAxis(i, axes[i], false);
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Returns the index of the specified axis, or {@code -1} if the axis
     
* is not assigned to the plot.
     
*
     
* @param axis
  
the axis ({@code null} not permitted).
     
*
     
* @return The axis index.
     
*
     
* @see #getDomainAxis(int)
     
* @see #getRangeAxisIndex(ValueAxis)
     
*
     
* @since 1.0.3
     
*/

    
public int getDomainAxisIndex(CategoryAxis axis) {
        
Args.nullNotPermitted(axis, "axis");
        
for (Entry<Integer, CategoryAxis> entry : this.domainAxes.entrySet()) {
            
if (entry.getValue() == axis) {
                
return entry.getKey();
            
}
        
}
        
return -1;
    
}

    
/**
     
* Returns the domain axis location for the primary domain axis.
     
*
     
* @return The location (never {@code null}).
     
*
     
*
 

     
*/

    
public AxisLocation getDomainAxisLocation() {
        
return getDomainAxisLocation(0);
    
}

    
/**
     
* Returns the location for a domain axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The location.
     
*
     
* @see #setDomainAxisLocation(int, AxisLocation)
     
*/

    
public AxisLocation getDomainAxisLocation(int index) {
        
AxisLocation result = this.domainAxisLocations.get(index);
        
if (result == null) {
            
result = AxisLocation.getOpposite(getDomainAxisLocation(0));
        
}
        
return result;
    
}

    
/**
     
* Sets the location of the domain axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param location
  
the axis location ({@code null} not permitted).
     
*
     
*
 

     
* @see #setDomainAxisLocation(int, AxisLocation)
     
*/

    
public void setDomainAxisLocation(AxisLocation location) {
        
// delegate...
        
setDomainAxisLocation(0, location, true);
    
}

    
/**
     
* Sets the location of the domain axis and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param location
  
the axis location ({@code null} not permitted).
     
* @param notify
  
a flag that controls whether listeners are notified.
     
*/

    
public void setDomainAxisLocation(AxisLocation location, boolean notify) {
        
// delegate...
        
setDomainAxisLocation(0, location, notify);
    
}

    
/**
     
* Sets the location for a domain axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param location
  
the location.
     
*
     
* @see #getDomainAxisLocation(int)
     
* @see #setRangeAxisLocation(int, AxisLocation)
     
*/

    
public void setDomainAxisLocation(int index, AxisLocation location) {
        
// delegate...
        
setDomainAxisLocation(index, location, true);
    
}

    
/**
     
* Sets the location for a domain axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param location
  
the location.
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.5
     
*
     
* @see #getDomainAxisLocation(int)
     
* @see #setRangeAxisLocation(int, AxisLocation, boolean)
     
*/

    
public void setDomainAxisLocation(int index, AxisLocation location,
            
boolean notify) {
        
if (index == 0 && location == null) {
            
throw new IllegalArgumentException(
                    
"Null 'location' for index 0 not permitted.");
        
}
        
this.domainAxisLocations.put(index, location);
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the domain axis edge.
  
This is derived from the axis location
     
* and the plot orientation.
     
*
     
* @return The edge (never {@code null}).
     
*/

    
public RectangleEdge getDomainAxisEdge() {
        
return getDomainAxisEdge(0);
    
}

    
/**
     
* Returns the edge for a domain axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The edge (never {@code null}).
     
*/

    
public RectangleEdge getDomainAxisEdge(int index) {
        
RectangleEdge result;
        
AxisLocation location = getDomainAxisLocation(index);
        
if (location != null) {
            
result = Plot.resolveDomainAxisLocation(location, this.orientation);
        
} else {
            
result = RectangleEdge.opposite(getDomainAxisEdge(0));
        
}
        
return result;
    
}

    
/**
     
* Returns the number of domain axes.
     
*
     
* @return The axis count.
     
*/

    
public int getDomainAxisCount() {
        
return this.domainAxes.size();
    
}

    
/**
     
* Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*/

    
public void clearDomainAxes() {
        
for (CategoryAxis xAxis : this.domainAxes.values()) {
            
if (xAxis != null) {
                
xAxis.removeChangeListener(this);
            
}
        
}
        
this.domainAxes.clear();
        
fireChangeEvent();
    
}

    
/**
     
* Configures the domain axes.
     
*/
    
public void configureDomainAxes() {
        
for (CategoryAxis xAxis : this.domainAxes.values()) {
            
if (xAxis != null) {
                
xAxis.configure();
            
}
        
}
    
}

    
/**
     
* Returns the range axis for the plot.
  
If the range axis for this plot is
     
* null, then the method will return the parent plot's range axis (if there
     
* is a parent plot).
     
*
     
* @return The range axis (possibly {@code null}).
     
*/

    
public ValueAxis getRangeAxis() {
        
return getRangeAxis(0);
    
}

    
/**
     
* Returns a range axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The axis ({@code null} possible).
     
*/

    
public ValueAxis getRangeAxis(int index) {
        
ValueAxis result = this.rangeAxes.get(index);
        
if (result == null) {
            
Plot parent = getParent();
            
if (parent instanceof CategoryPlot) {
                
CategoryPlot cp = (CategoryPlot) parent;
                
result = cp.getRangeAxis(index);
            
}
        
}
        
return result;
    
}

    
/**
     
* Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param axis
  
the axis ({@code null} permitted).
     
*/

    
public void setRangeAxis(ValueAxis axis) {
        
setRangeAxis(0, axis);
    
}

    
/**
     
* Sets a range axis and sends a {@link PlotChangeEvent} to all registered
     
* listeners.
     
*
     
* @param index
  
the axis index.
     
* @param axis
  
the axis.
     
*/

    
public void setRangeAxis(int index, ValueAxis axis) {
        
setRangeAxis(index, axis, true);
    
}

    
/**
     
* Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param axis
  
the axis.
     
* @param notify
  
notify listeners?
     
*/

    
public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
        
ValueAxis existing = this.rangeAxes.get(index);
        
if (existing != null) {
            
existing.removeChangeListener(this);
        
}
        
if (axis != null) {
            
axis.setPlot(this);
        
}
        
this.rangeAxes.put(index, axis);
        
if (axis != null) {
            
axis.configure();
            
axis.addChangeListener(this);
        
}
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Sets the range axes for this plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param axes
  
the axes ({@code null} not permitted).
     
*
     
* @see #setDomainAxes(CategoryAxis[])
     
*/

    
public void setRangeAxes(ValueAxis[] axes) {
        
for (int i = 0; i < axes.length; i++) {
            
setRangeAxis(i, axes[i], false);
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Returns the index of the specified axis, or {@code -1} if the axis
     
* is not assigned to the plot.
     
*
     
* @param axis
  
the axis ({@code null} not permitted).
     
*
     
* @return The axis index.
     
*
     
* @see #getRangeAxis(int)
     
* @see #getDomainAxisIndex(CategoryAxis)
     
*
     
* @since 1.0.7
     
*/

    
public int getRangeAxisIndex(ValueAxis axis) {
        
Args.nullNotPermitted(axis, "axis");
        
int result = findRangeAxisIndex(axis);
        
if (result < 0) { // try the parent plot
            
Plot parent = getParent();
            
if (parent instanceof CategoryPlot) {
                
CategoryPlot p = (CategoryPlot) parent;
                
result = p.getRangeAxisIndex(axis);
            
}
        
}
        
return result;
    
}

    
private int findRangeAxisIndex(ValueAxis axis) {
        
for (Entry<Integer, ValueAxis> entry : this.rangeAxes.entrySet()) {
            
if (entry.getValue() == axis) {
                
return entry.getKey();
            
}
        
}
        
return -1;
    
}
    

    
/**
     
* Returns the range axis location.
     
*
     
* @return The location (never {@code null}).
     
*/

    
public AxisLocation getRangeAxisLocation() {
        
return getRangeAxisLocation(0);
    
}

    
/**
     
* Returns the location for a range axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The location.
     
*
     
* @see #setRangeAxisLocation(int, AxisLocation)
     
*/

    
public AxisLocation getRangeAxisLocation(int index) {
        
AxisLocation result = this.rangeAxisLocations.get(index);
        
if (result == null) {
            
result = AxisLocation.getOpposite(getRangeAxisLocation(0));
        
}
        
return result;
    
}

    
/**
     
* Sets the location of the range axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param location
  
the location ({@code null} not permitted).
     
*
     
* @see #setRangeAxisLocation(AxisLocation, boolean)
     
* @see #setDomainAxisLocation(AxisLocation)
     
*/

    
public void setRangeAxisLocation(AxisLocation location) {
        
// defer argument checking...
        
setRangeAxisLocation(location, true);
    
}

    
/**
     
* Sets the location of the range axis and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param location
  
the location ({@code null} not permitted).
     
* @param notify
  
notify listeners?
     
*
     
* @see #setDomainAxisLocation(AxisLocation, boolean)
     
*/

    
public void setRangeAxisLocation(AxisLocation location, boolean notify) {
        
setRangeAxisLocation(0, location, notify);
    
}

    
/**
     
* Sets the location for a range axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param location
  
the location.
     
*
     
* @see #getRangeAxisLocation(int)
     
* @see #setRangeAxisLocation(int, AxisLocation, boolean)
     
*/

    
public void setRangeAxisLocation(int index, AxisLocation location) {
        
setRangeAxisLocation(index, location, true);
    
}

    
/**
     
* Sets the location for a range axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param index
  
the axis index.
     
* @param location
  
the location.
     
* @param notify
  
notify listeners?
     
*
     
* @see #getRangeAxisLocation(int)
     
* @see #setDomainAxisLocation(int, AxisLocation, boolean)
     
*/

    
public void setRangeAxisLocation(int index, AxisLocation location,
            
boolean notify) {
        
if (index == 0 && location == null) {
            
throw new IllegalArgumentException(
                    
"Null 'location' for index 0 not permitted.");
        
}
        
this.rangeAxisLocations.put(index, location);
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the edge where the primary range axis is located.
     
*
     
* @return The edge (never {@code null}).
     
*/

    
public RectangleEdge getRangeAxisEdge() {
        
return getRangeAxisEdge(0);
    
}

    
/**
     
* Returns the edge for a range axis.
     
*
     
* @param index
  
the axis index.
     
*
     
* @return The edge.
     
*/

    
public RectangleEdge getRangeAxisEdge(int index) {
        
AxisLocation location = getRangeAxisLocation(index);
        
return Plot.resolveRangeAxisLocation(location, this.orientation);
    
}

    
/**
     
* Returns the number of range axes.
     
*
     
* @return The axis count.
     
*/

    
public int getRangeAxisCount() {
        
return this.rangeAxes.size();
    
}

    
/**
     
* Clears the range axes from the plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*/

    
public void clearRangeAxes() {
        
for (ValueAxis yAxis : this.rangeAxes.values()) {
            
if (yAxis != null) {
                
yAxis.removeChangeListener(this);
            
}
        
}
        
this.rangeAxes.clear();
        
fireChangeEvent();
    
}

    
/**
     
* Configures the range axes.
     
*/
    
public void configureRangeAxes() {
        
for (ValueAxis yAxis : this.rangeAxes.values()) {
            
if (yAxis != null) {
                
yAxis.configure();
            
}
        
}
    
}

    
/**
     
* Returns the primary dataset for the plot.
     
*
     
* @return The primary dataset (possibly {@code null}).
     
*
     
* @see #setDataset(CategoryDataset)
     
*/

    
public CategoryDataset getDataset() {
        
return getDataset(0);
    
}

    
/**
     
* Returns the dataset with the given index, or {@code null} if there is
     
* no dataset.
     
*
     
* @param index
  
the dataset index (must be &gt;= 0).
     
*
     
* @return The dataset (possibly {@code null}).
     
*
     
* @see #setDataset(int, CategoryDataset)
     
*/

    
public CategoryDataset getDataset(int index) {
        
return this.datasets.get(index);
    
}

    
/**
     
* Sets the dataset for the plot, replacing the existing dataset, if there
     
* is one.
  
This method also calls the
     
* {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
     
* axis ranges if necessary and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*
     
* @param dataset
  
the dataset ({@code null} permitted).
     
*
     
*
 

     
*/

    
public void setDataset(CategoryDataset dataset) {
        
setDataset(0, dataset);
    
}

    
/**
     
* Sets a dataset for the plot and sends a change notification to all
     
* registered listeners.
     
*
     
* @param index
  
the dataset index (must be &gt;= 0).
     
* @param dataset
  
the dataset ({@code null} permitted).
     
*
     
* @see #getDataset(int)
     
*/

    
public void setDataset(int index, CategoryDataset dataset) {
        
CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
        
if (existing != null) {
            
existing.removeChangeListener(this);
        
}
        
this.datasets.put(index, dataset);
        
if (dataset != null) {
            
dataset.addChangeListener(this);
        
}
        
// send a dataset change event to self...
        
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
        
datasetChanged(event);
    
}

    
/**
     
* Returns the number of datasets.
     
*
     
* @return The number of datasets.
     
*
     
* @since 1.0.2
     
*/

    
public int getDatasetCount() {
        
return this.datasets.size();
    
}

    
/**
     
* Returns the index of the specified dataset, or {@code -1} if the
     
* dataset does not belong to the plot.
     
*
     
* @param dataset
  
the dataset ({@code null} not permitted).
     
*
     
* @return The index.
     
*
     
* @since 1.0.11
     
*/

    
public int indexOf(CategoryDataset dataset) {
        
for (Entry<Integer, CategoryDataset> entry: this.datasets.entrySet()) {
            
if (entry.getValue() == dataset) {
                
return entry.getKey();
            
}
        
}
        
return -1;
    
}

    
/**
     
* Maps a dataset to a particular domain axis.
     
*
     
* @param index
  
the dataset index (zero-based).
     
* @param axisIndex
  
the axis index (zero-based).
     
*
     
* @see #getDomainAxisForDataset(int)
     
*/

    
public void mapDatasetToDomainAxis(int index, int axisIndex) {
        
List<Integer> axisIndices = new java.util.ArrayList<Integer>(1);
        
axisIndices.add(axisIndex);
        
mapDatasetToDomainAxes(index, axisIndices);
    
}

    
/**
     
* Maps the specified dataset to the axes in the list.
  
Note that the
     
* conversion of data values into Java2D space is always performed using
     
* the first axis in the list.
     
*
     
* @param index
  
the dataset index (zero-based).
     
* @param axisIndices
  
the axis indices ({@code null} permitted).
     
*
     
* @since 1.0.12
     
*/

    
public void mapDatasetToDomainAxes(int index, List axisIndices) {
        
Args.requireNonNegative(index, "index");
        
checkAxisIndices(axisIndices);
        
this.datasetToDomainAxesMap.put(index, new ArrayList(axisIndices));
        
// fake a dataset change event to update axes...
        
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
    
}

    
/**
     
* This method is used to perform argument checking on the list of
     
* axis indices passed to mapDatasetToDomainAxes() and
     
* mapDatasetToRangeAxes().
     
*
     
* @param indices
  
the list of indices ({@code null} permitted).
     
*/

    
private void checkAxisIndices(List indices) {
        
// axisIndices can be:
        
// 1.
  
null;
        
// 2.
  
non-empty, containing only Integer objects that are unique.

        
if (indices == null) {
            
return;
  
// OK
        
}
        
int count = indices.size();
        
if (count == 0) {
            
throw new IllegalArgumentException("Empty list not permitted.");
        
}
        
HashSet set = new HashSet();
        
for (int i = 0; i < count; i++) {
            
Object item = indices.get(i);
            
if (!(item instanceof Integer)) {
                
throw new IllegalArgumentException(
                        
"Indices must be Integer instances.");
            
}
            
if (set.contains(item)) {
                
throw new IllegalArgumentException("Indices must be unique.");
            
}
            
set.add(item);
        
}
    
}

    
/**
     
* Returns the domain axis for a dataset.
  
You can change the axis for a
     
* dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
     
*
     
* @param index
  
the dataset index (must be &gt;= 0).
     
*
     
* @return The domain axis.
     
*
     
* @see #mapDatasetToDomainAxis(int, int)
     
*/

    
public CategoryAxis getDomainAxisForDataset(int index) {
        
Args.requireNonNegative(index, "index");
        
CategoryAxis axis;
        
List axisIndices = (List) this.datasetToDomainAxesMap.get(
                
new Integer(index));
        
if (axisIndices != null) {
            
// the first axis in the list is used for data <--> Java2D
            
Integer axisIndex = (Integer) axisIndices.get(0);
            
axis = getDomainAxis(axisIndex.intValue());
        
} else {
            
axis = getDomainAxis(0);
        
}
        
return axis;
    
}

    
/**
     
* Maps a dataset to a particular range axis.
     
*
     
* @param index
  
the dataset index (zero-based).
     
* @param axisIndex
  
the axis index (zero-based).
     
*
     
* @see #getRangeAxisForDataset(int)
     
*/

    
public void mapDatasetToRangeAxis(int index, int axisIndex) {
        
List axisIndices = new java.util.ArrayList(1);
        
axisIndices.add(new Integer(axisIndex));
        
mapDatasetToRangeAxes(index, axisIndices);
    
}

    
/**
     
* Maps the specified dataset to the axes in the list.
  
Note that the
     
* conversion of data values into Java2D space is always performed using
     
* the first axis in the list.
     
*
     
* @param index
  
the dataset index (zero-based).
     
* @param axisIndices
  
the axis indices ({@code null} permitted).
     
*
     
* @since 1.0.12
     
*/

    
public void mapDatasetToRangeAxes(int index, List axisIndices) {
        
Args.requireNonNegative(index, "index");
        
checkAxisIndices(axisIndices);
        
this.datasetToRangeAxesMap.put(index, new ArrayList(axisIndices));
        
// fake a dataset change event to update axes...
        
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
    
}

    
/**
     
* Returns the range axis for a dataset.
  
You can change the axis for a
     
* dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
     
*
     
* @param index
  
the dataset index (must be &gt;= 0).
     
*
     
* @return The range axis.
     
*
     
* @see #mapDatasetToRangeAxis(int, int)
     
*/

    
public ValueAxis getRangeAxisForDataset(int index) {
        
Args.requireNonNegative(index, "index");
        
ValueAxis axis;
        
List axisIndices = (List) this.datasetToRangeAxesMap.get(
                
new Integer(index));
        
if (axisIndices != null) {
            
// the first axis in the list is used for data <--> Java2D
            
Integer axisIndex = (Integer) axisIndices.get(0);
            
axis = getRangeAxis(axisIndex.intValue());
        
} else {
            
axis = getRangeAxis(0);
        
}
        
return axis;
    
}

    
/**
     
* Returns the number of renderer slots for this plot.
     
*
     
* @return The number of renderer slots.
     
*
     
* @since 1.0.11
     
*/

    
public int getRendererCount() {
        
return this.renderers.size();
    
}

    
/**
     
* Returns a reference to the renderer for the plot.
     
*
     
* @return The renderer.
     
*
     
* @see #setRenderer(CategoryItemRenderer)
     
*/

    
public CategoryItemRenderer getRenderer() {
        
return getRenderer(0);
    
}

    
/**
     
* Returns the renderer at the given index.
     
*
     
* @param index
  
the renderer index.
     
*
     
* @return The renderer (possibly {@code null}).
     
*
     
* @see #setRenderer(int, CategoryItemRenderer)
     
*/

    
public CategoryItemRenderer getRenderer(int index) {
        
CategoryItemRenderer renderer = this.renderers.get(index);
        
if (renderer == null) {
            
return this.renderers.get(0);
        
}
        
return renderer;
    
}

    
/**
     
* Sets the renderer at index 0 (sometimes referred to as the "primary"
     
* renderer) and sends a change event to all registered listeners.
     
*
     
* @param renderer
  
the renderer ({@code null} permitted.
     
*
     
*
 

     
*/

    
public void setRenderer(CategoryItemRenderer renderer) {
        
setRenderer(0, renderer, true);
    
}

    
/**
     
* Sets the renderer at index 0 (sometimes referred to as the "primary"
     
* renderer) and, if requested, sends a change event to all registered
 

     
* listeners.
     
* <p>
     
* You can set the renderer to {@code null}, but this is not
     
* recommended because:
     
* <ul>
     
*
   
<li>no data will be displayed;</li>
     
*
   
<li>the plot background will not be painted;</li>
     
* </ul>
     
*
     
* @param renderer
  
the renderer ({@code null} permitted).
     
* @param notify
  
notify listeners?
     
*
     
*
 

     
*/

    
public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
        
setRenderer(0, renderer, notify);
    
}

    
/**
     
* Sets the renderer to use for the dataset with the specified index and
     
* sends a change event to all registered listeners.
  
Note that each
     
* dataset should have its own renderer, you should not use one renderer
     
* for multiple datasets.
     
*
     
* @param index
  
the index.
     
* @param renderer
  
the renderer ({@code null} permitted).
     
*
     
* @see #getRenderer(int)
     
* @see #setRenderer(int, CategoryItemRenderer, boolean)
     
*/

    
public void setRenderer(int index, CategoryItemRenderer renderer) {
        
setRenderer(index, renderer, true);
    
}

    
/**
     
* Sets the renderer to use for the dataset with the specified index and,
     
* if requested, sends a change event to all registered listeners.
  
Note
 

     
* that each dataset should have its own renderer, you should not use one
 

     
* renderer for multiple datasets.
     
*
     
* @param index
  
the index.
     
* @param renderer
  
the renderer ({@code null} permitted).
     
* @param notify
  
notify listeners?
     
*
     
* @see #getRenderer(int)
     
*/

    
public void setRenderer(int index, CategoryItemRenderer renderer,
            
boolean notify) {
        
CategoryItemRenderer existing = this.renderers.get(index);
        
if (existing != null) {
            
existing.removeChangeListener(this);
        
}
        
this.renderers.put(index, renderer);
        
if (renderer != null) {
            
renderer.setPlot(this);
            
renderer.addChangeListener(this);
        
}
        
configureDomainAxes();
        
configureRangeAxes();
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Sets the renderers for this plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param renderers
  
the renderers.
     
*/

    
public void setRenderers(CategoryItemRenderer[] renderers) {
        
for (int i = 0; i < renderers.length; i++) {
            
setRenderer(i, renderers[i], false);
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Returns the renderer for the specified dataset.
  
If the dataset doesn't
     
* belong to the plot, this method will return {@code null}.
     
*
     
* @param dataset
  
the dataset ({@code null} permitted).
     
*
     
* @return The renderer (possibly {@code null}).
     
*/

    
public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
        
int datasetIndex = indexOf(dataset);
        
if (datasetIndex < 0) {
            
return null;
        
}
        
CategoryItemRenderer renderer = this.renderers.get(datasetIndex);
        
if (renderer == null) {
            
return getRenderer();
        
}
        
return renderer;
    
}

    
/**
     
* Returns the index of the specified renderer, or {@code -1} if the
     
* renderer is not assigned to this plot.
     
*
     
* @param renderer
  
the renderer ({@code null} permitted).
     
*
     
* @return The renderer index.
     
*/

    
public int getIndexOf(CategoryItemRenderer renderer) {
        
for (Entry<Integer, CategoryItemRenderer> entry
                
: this.renderers.entrySet()) {
            
if (entry.getValue() == renderer) {
                
return entry.getKey();
            
}
        
}
        
return -1;
    
}

    
/**
     
* Returns the dataset rendering order.
     
*
     
* @return The order (never {@code null}).
     
*
     
* @see #setDatasetRenderingOrder(DatasetRenderingOrder)
     
*/

    
public DatasetRenderingOrder getDatasetRenderingOrder() {
        
return this.renderingOrder;
    
}

    
/**
     
* Sets the rendering order and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
  
By default, the plot renders the primary dataset
     
* last (so that the primary dataset overlays the secondary datasets).
  
You
     
* can reverse this if you want to.
     
*
     
* @param order
  
the rendering order ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
        
Args.nullNotPermitted(order, "order");
        
this.renderingOrder = order;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the order in which the columns are rendered.
  
The default value
     
* is {@code SortOrder.ASCENDING}.
     
*
     
* @return The column rendering order (never {@code null}).
     
*
     
* @see #setColumnRenderingOrder(SortOrder)
     
*/

    
public SortOrder getColumnRenderingOrder() {
        
return this.columnRenderingOrder;
    
}

    
/**
     
* Sets the column order in which the items in each dataset should be
     
* rendered and sends a {@link PlotChangeEvent} to all registered
     
* listeners.
  
Note that this affects the order in which items are drawn,
     
* NOT their position in the chart.
     
*
     
* @param order
  
the order ({@code null} not permitted).
     
*
     
*
 

     
* @see #setRowRenderingOrder(SortOrder)
     
*/

    
public void setColumnRenderingOrder(SortOrder order) {
        
Args.nullNotPermitted(order, "order");
        
this.columnRenderingOrder = order;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the order in which the rows should be rendered.
  
The default
     
* value is {@code SortOrder.ASCENDING}.
     
*
     
* @return The order (never {@code null}).
     
*
     
* @see #setRowRenderingOrder(SortOrder)
     
*/

    
public SortOrder getRowRenderingOrder() {
        
return this.rowRenderingOrder;
    
}

    
/**
     
* Sets the row order in which the items in each dataset should be
     
* rendered and sends a {@link PlotChangeEvent} to all registered
     
* listeners.
  
Note that this affects the order in which items are drawn,
     
* NOT their position in the chart.
     
*
     
* @param order
  
the order ({@code null} not permitted).
     
*
     
*
 

     
* @see #setColumnRenderingOrder(SortOrder)
     
*/

    
public void setRowRenderingOrder(SortOrder order) {
        
Args.nullNotPermitted(order, "order");
        
this.rowRenderingOrder = order;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the flag that controls whether the domain grid-lines are visible.
     
*
     
* @return The {@code true} or {@code false}.
     
*
     
* @see #setDomainGridlinesVisible(boolean)
     
*/

    
public boolean isDomainGridlinesVisible() {
        
return this.domainGridlinesVisible;
    
}

    
/**
     
* Sets the flag that controls whether or not grid-lines are drawn against
     
* the domain axis.
     
* <p>
     
* If the flag value changes, a {@link PlotChangeEvent} is sent to all
     
* registered listeners.
     
*
     
* @param visible
  
the new value of the flag.
     
*
     
*
 

     
*/

    
public void setDomainGridlinesVisible(boolean visible) {
        
if (this.domainGridlinesVisible != visible) {
            
this.domainGridlinesVisible = visible;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the position used for the domain gridlines.
     
*
     
* @return The gridline position (never {@code null}).
     
*
     
* @see #setDomainGridlinePosition(CategoryAnchor)
     
*/

    
public CategoryAnchor getDomainGridlinePosition() {
        
return this.domainGridlinePosition;
    
}

    
/**
     
* Sets the position used for the domain gridlines and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param position
  
the position ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setDomainGridlinePosition(CategoryAnchor position) {
        
Args.nullNotPermitted(position, "position");
        
this.domainGridlinePosition = position;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the stroke used to draw grid-lines against the domain axis.
     
*
     
* @return The stroke (never {@code null}).
     
*
     
* @see #setDomainGridlineStroke(Stroke)
     
*/

    
public Stroke getDomainGridlineStroke() {
        
return this.domainGridlineStroke;
    
}

    
/**
     
* Sets the stroke used to draw grid-lines against the domain axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param stroke
  
the stroke ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setDomainGridlineStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.domainGridlineStroke = stroke;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the paint used to draw grid-lines against the domain axis.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @see #setDomainGridlinePaint(Paint)
     
*/

    
public Paint getDomainGridlinePaint() {
        
return this.domainGridlinePaint;
    
}

    
/**
     
* Sets the paint used to draw the grid-lines (if any) against the domain
     
* axis and sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setDomainGridlinePaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.domainGridlinePaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns a flag that controls whether or not a zero baseline is
     
* displayed for the range axis.
     
*
     
* @return A boolean.
     
*
     
* @see #setRangeZeroBaselineVisible(boolean)
     
*
     
* @since 1.0.13
     
*/

    
public boolean isRangeZeroBaselineVisible() {
        
return this.rangeZeroBaselineVisible;
    
}

    
/**
     
* Sets the flag that controls whether or not the zero baseline is
     
* displayed for the range axis, and sends a {@link PlotChangeEvent} to
     
* all registered listeners.
     
*
     
* @param visible
  
the flag.
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeZeroBaselineVisible(boolean visible) {
        
this.rangeZeroBaselineVisible = visible;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the stroke used for the zero baseline against the range axis.
     
*
     
* @return The stroke (never {@code null}).
     
*
     
* @see #setRangeZeroBaselineStroke(Stroke)
     
*
     
* @since 1.0.13
     
*/

    
public Stroke getRangeZeroBaselineStroke() {
        
return this.rangeZeroBaselineStroke;
    
}

    
/**
     
* Sets the stroke for the zero baseline for the range axis,
     
* and sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param stroke
  
the stroke ({@code null} not permitted).
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeZeroBaselineStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.rangeZeroBaselineStroke = stroke;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the paint for the zero baseline (if any) plotted against the
     
* range axis.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @see #setRangeZeroBaselinePaint(Paint)
     
*
     
* @since 1.0.13
     
*/

    
public Paint getRangeZeroBaselinePaint() {
        
return this.rangeZeroBaselinePaint;
    
}

    
/**
     
* Sets the paint for the zero baseline plotted against the range axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeZeroBaselinePaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.rangeZeroBaselinePaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the flag that controls whether the range grid-lines are visible.
     
*
     
* @return The flag.
     
*
     
* @see #setRangeGridlinesVisible(boolean)
     
*/

    
public boolean isRangeGridlinesVisible() {
        
return this.rangeGridlinesVisible;
    
}

    
/**
     
* Sets the flag that controls whether or not grid-lines are drawn against
     
* the range axis.
  
If the flag changes value, a {@link PlotChangeEvent} is
     
* sent to all registered listeners.
     
*
     
* @param visible
  
the new value of the flag.
     
*
     
*
 

     
*/

    
public void setRangeGridlinesVisible(boolean visible) {
        
if (this.rangeGridlinesVisible != visible) {
            
this.rangeGridlinesVisible = visible;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the stroke used to draw the grid-lines against the range axis.
     
*
     
* @return The stroke (never {@code null}).
     
*
     
* @see #setRangeGridlineStroke(Stroke)
     
*/

    
public Stroke getRangeGridlineStroke() {
        
return this.rangeGridlineStroke;
    
}

    
/**
     
* Sets the stroke used to draw the grid-lines against the range axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param stroke
  
the stroke ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setRangeGridlineStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.rangeGridlineStroke = stroke;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the paint used to draw the grid-lines against the range axis.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @see #setRangeGridlinePaint(Paint)
     
*/

    
public Paint getRangeGridlinePaint() {
        
return this.rangeGridlinePaint;
    
}

    
/**
     
* Sets the paint used to draw the grid lines against the range axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setRangeGridlinePaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.rangeGridlinePaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns {@code true} if the range axis minor grid is visible, and
     
* {@code false} otherwise.
     
*
     
* @return A boolean.
     
*
     
* @see #setRangeMinorGridlinesVisible(boolean)
     
*
     
* @since 1.0.13
     
*/

    
public boolean isRangeMinorGridlinesVisible() {
        
return this.rangeMinorGridlinesVisible;
    
}

    
/**
     
* Sets the flag that controls whether or not the range axis minor grid
     
* lines are visible.
     
* <p>
     
* If the flag value is changed, a {@link PlotChangeEvent} is sent to all
     
* registered listeners.
     
*
     
* @param visible
  
the new value of the flag.
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeMinorGridlinesVisible(boolean visible) {
        
if (this.rangeMinorGridlinesVisible != visible) {
            
this.rangeMinorGridlinesVisible = visible;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the stroke for the minor grid lines (if any) plotted against the
     
* range axis.
     
*
     
* @return The stroke (never {@code null}).
     
*
     
* @see #setRangeMinorGridlineStroke(Stroke)
     
*
     
* @since 1.0.13
     
*/

    
public Stroke getRangeMinorGridlineStroke() {
        
return this.rangeMinorGridlineStroke;
    
}

    
/**
     
* Sets the stroke for the minor grid lines plotted against the range axis,
     
* and sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param stroke
  
the stroke ({@code null} not permitted).
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeMinorGridlineStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.rangeMinorGridlineStroke = stroke;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the paint for the minor grid lines (if any) plotted against the
     
* range axis.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @see #setRangeMinorGridlinePaint(Paint)
     
*
     
* @since 1.0.13
     
*/

    
public Paint getRangeMinorGridlinePaint() {
        
return this.rangeMinorGridlinePaint;
    
}

    
/**
     
* Sets the paint for the minor grid lines plotted against the range axis
     
* and sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
*
 

     
*
     
* @since 1.0.13
     
*/

    
public void setRangeMinorGridlinePaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.rangeMinorGridlinePaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the fixed legend items, if any.
     
*
     
* @return The legend items (possibly {@code null}).
     
*
     
* @see #setFixedLegendItems(LegendItemCollection)
     
*/

    
public LegendItemCollection getFixedLegendItems() {
        
return this.fixedLegendItems;
    
}

    
/**
     
* Sets the fixed legend items for the plot.
  
Leave this set to
     
* {@code null} if you prefer the legend items to be created
     
* automatically.
     
*
     
* @param items
  
the legend items ({@code null} permitted).
     
*
     
*
 

     
*/

    
public void setFixedLegendItems(LegendItemCollection items) {
        
this.fixedLegendItems = items;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the legend items for the plot.
  
By default, this method creates
     
* a legend item for each series in each of the datasets.
  
You can change
     
* this behaviour by overriding this method.
     
*
     
* @return The legend items.
     
*/

    
@Override
    
public LegendItemCollection getLegendItems() {
        
if (this.fixedLegendItems != null) {
            
return this.fixedLegendItems;
        
}
        
LegendItemCollection result = new LegendItemCollection();
        
// get the legend items for the datasets...
        
for (CategoryDataset dataset: this.datasets.values()) {
            
if (dataset != null) {
                
int datasetIndex = indexOf(dataset);
                
CategoryItemRenderer renderer = getRenderer(datasetIndex);
                
if (renderer != null) {
                    
result.addAll(renderer.getLegendItems());
                
}
            
}
        
}
        
return result;
    
}

    
/**
     
* Handles a 'click' on the plot by updating the anchor value.
     
*
     
* @param x
  
x-coordinate of the click (in Java2D space).
     
* @param y
  
y-coordinate of the click (in Java2D space).
     
* @param info
  
information about the plot's dimensions.
     
*
     
*/

    
@Override
    
public void handleClick(int x, int y, PlotRenderingInfo info) {

        
Rectangle2D dataArea = info.getDataArea();
        
if (dataArea.contains(x, y)) {
            
// set the anchor value for the range axis...
            
double java2D = 0.0;
            
if (this.orientation == PlotOrientation.HORIZONTAL) {
                
java2D = x;
            
} else if (this.orientation == PlotOrientation.VERTICAL) {
                
java2D = y;
            
}
            
RectangleEdge edge = Plot.resolveRangeAxisLocation(
                    
getRangeAxisLocation(), this.orientation);
            
double value = getRangeAxis().java2DToValue(
                    
java2D, info.getDataArea(), edge);
            
setAnchorValue(value);
            
setRangeCrosshairValue(value);
        
}

    
}

    
/**
     
* Zooms (in or out) on the plot's value axis.
     
* <p>
     
* If the value 0.0 is passed in as the zoom percent, the auto-range
     
* calculation for the axis is restored (which sets the range to include
     
* the minimum and maximum data values, thus displaying all the data).
     
*
     
* @param percent
  
the zoom amount.
     
*/

    
@Override
    
public void zoom(double percent) {
        
if (percent > 0.0) {
            
double range = getRangeAxis().getRange().getLength();
            
double scaledRange = range * percent;
            
getRangeAxis().setRange(this.anchorValue - scaledRange / 2.0,
                    
this.anchorValue + scaledRange / 2.0);
        
}
        
else {
            
getRangeAxis().setAutoRange(true);
        
}
    
}

    
/**
     
* Receives notification of a change to an {@link Annotation} added to
     
* this plot.
     
*
     
* @param event
  
information about the event (not used here).
     
*
     
* @since 1.0.14
     
*/

    
@Override
    
public void annotationChanged(AnnotationChangeEvent event) {
        
if (getParent() != null) {
            
getParent().annotationChanged(event);
        
} else {
            
PlotChangeEvent e = new PlotChangeEvent(this);
            
notifyListeners(e);
        
}
    
}

    
/**
     
* Receives notification of a change to the plot's dataset.
     
* <P>
     
* The range axis bounds will be recalculated if necessary.
     
*
     
* @param event
  
information about the event (not used here).
     
*/

    
@Override
    
public void datasetChanged(DatasetChangeEvent event) {
        
for (ValueAxis yAxis : this.rangeAxes.values()) {
            
if (yAxis != null) {
                
yAxis.configure();
            
}
        
}
        
if (getParent() != null) {
            
getParent().datasetChanged(event);
        
} else {
            
PlotChangeEvent e = new PlotChangeEvent(this);
            
e.setType(ChartChangeEventType.DATASET_UPDATED);
            
notifyListeners(e);
        
}

    
}

    
/**
     
* Receives notification of a renderer change event.
     
*
     
* @param event
  
the event.
     
*/

    
@Override
    
public void rendererChanged(RendererChangeEvent event) {
        
Plot parent = getParent();
        
if (parent != null) {
            
if (parent instanceof RendererChangeListener) {
                
RendererChangeListener rcl = (RendererChangeListener) parent;
                
rcl.rendererChanged(event);
            
} else {
                
// this should never happen with the existing code, but throw
                
// an exception in case future changes make it possible...
                
throw new RuntimeException(
                    
"The renderer has changed and I don't know what to do!");
            
}
        
} else {
            
configureRangeAxes();
            
PlotChangeEvent e = new PlotChangeEvent(this);
            
notifyListeners(e);
        
}
    
}

    
/**
     
* Adds a marker for display (in the foreground) against the domain axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners. Typically a
     
* marker will be drawn by the renderer as a line perpendicular to the
     
* domain axis, however this is entirely up to the renderer.
     
*
     
* @param marker
  
the marker ({@code null} not permitted).
     
*
     
* @see #removeDomainMarker(Marker)
     
*/

    
public void addDomainMarker(CategoryMarker marker) {
        
addDomainMarker(marker, Layer.FOREGROUND);
    
}

    
/**
     
* Adds a marker for display against the domain axis and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
  
Typically a marker
     
* will be drawn by the renderer as a line perpendicular to the domain
     
* axis, however this is entirely up to the renderer.
     
*
     
* @param marker
  
the marker ({@code null} not permitted).
     
* @param layer
  
the layer (foreground or background) ({@code null}
     
*
               
not permitted).
     
*
     
* @see #removeDomainMarker(Marker, Layer)
     
*/

    
public void addDomainMarker(CategoryMarker marker, Layer layer) {
        
addDomainMarker(0, marker, layer);
    
}

    
/**
     
* Adds a marker for display by a particular renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
* <P>
     
* Typically a marker will be drawn by the renderer as a line perpendicular
     
* to a domain axis, however this is entirely up to the renderer.
     
*
     
* @param index
  
the renderer index.
     
* @param marker
  
the marker ({@code null} not permitted).
     
* @param layer
  
the layer ({@code null} not permitted).
     
*
     
* @see #removeDomainMarker(int, Marker, Layer)
     
*/

    
public void addDomainMarker(int index, CategoryMarker marker, Layer layer) {
        
addDomainMarker(index, marker, layer, true);
    
}

    
/**
     
* Adds a marker for display by a particular renderer and, if requested,
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
* <P>
     
* Typically a marker will be drawn by the renderer as a line perpendicular
     
* to a domain axis, however this is entirely up to the renderer.
     
*
     
* @param index
  
the renderer index.
     
* @param marker
  
the marker ({@code null} not permitted).
     
* @param layer
  
the layer ({@code null} not permitted).
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.10
     
*
     
* @see #removeDomainMarker(int, Marker, Layer, boolean)
     
*/

    
public void addDomainMarker(int index, CategoryMarker marker, Layer layer,
            
boolean notify) {
        
Args.nullNotPermitted(marker, "marker");
        
Args.nullNotPermitted(layer, "layer");
        
Collection markers;
        
if (layer == Layer.FOREGROUND) {
            
markers = (Collection) this.foregroundDomainMarkers.get(
                    
new Integer(index));
            
if (markers == null) {
                
markers = new java.util.ArrayList();
                
this.foregroundDomainMarkers.put(new Integer(index), markers);
            
}
            
markers.add(marker);
        
} else if (layer == Layer.BACKGROUND) {
            
markers = (Collection) this.backgroundDomainMarkers.get(
                    
new Integer(index));
            
if (markers == null) {
                
markers = new java.util.ArrayList();
                
this.backgroundDomainMarkers.put(new Integer(index), markers);
            
}
            
markers.add(marker);
        
}
        
marker.addChangeListener(this);
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Clears all the domain markers for the plot and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
*
 

     
*/

    
public void clearDomainMarkers() {
        
if (this.backgroundDomainMarkers != null) {
            
Set keys = this.backgroundDomainMarkers.keySet();
            
Iterator iterator = keys.iterator();
            
while (iterator.hasNext()) {
                
Integer key = (Integer) iterator.next();
                
clearDomainMarkers(key.intValue());
            
}
            
this.backgroundDomainMarkers.clear();
        
}
        
if (this.foregroundDomainMarkers != null) {
            
Set keys = this.foregroundDomainMarkers.keySet();
            
Iterator iterator = keys.iterator();
            
while (iterator.hasNext()) {
                
Integer key = (Integer) iterator.next();
                
clearDomainMarkers(key.intValue());
            
}
            
this.foregroundDomainMarkers.clear();
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Returns the list of domain markers (read only) for the specified layer.
     
*
     
* @param layer
  
the layer (foreground or background).
     
*
     
* @return The list of domain markers.
     
*/

    
public Collection getDomainMarkers(Layer layer) {
        
return getDomainMarkers(0, layer);
    
}

    
/**
     
* Returns a collection of domain markers for a particular renderer and
     
* layer.
     
*
     
* @param index
  
the renderer index.
     
* @param layer
  
the layer.
     
*
     
* @return A collection of markers (possibly {@code null}).
     
*/

    
public Collection getDomainMarkers(int index, Layer layer) {
        
Collection result = null;
        
Integer key = new Integer(index);
        
if (layer == Layer.FOREGROUND) {
            
result = (Collection) this.foregroundDomainMarkers.get(key);
        
}
        
else if (layer == Layer.BACKGROUND) {
            
result = (Collection) this.backgroundDomainMarkers.get(key);
        
}
        
if (result != null) {
            
result = Collections.unmodifiableCollection(result);
        
}
        
return result;
    
}

    
/**
     
* Clears all the domain markers for the specified renderer.
     
*
     
* @param index
  
the renderer index.
     
*
     
* @see #clearRangeMarkers(int)
     
*/

    
public void clearDomainMarkers(int index) {
        
Integer key = new Integer(index);
        
if (this.backgroundDomainMarkers != null) {
            
Collection markers
                
= (Collection) this.backgroundDomainMarkers.get(key);
            
if (markers != null) {
                
Iterator iterator = markers.iterator();
                
while (iterator.hasNext()) {
                    
Marker m = (Marker) iterator.next();
                    
m.removeChangeListener(this);
                
}
                
markers.clear();
            
}
        
}
        
if (this.foregroundDomainMarkers != null) {
            
Collection markers
                
= (Collection) this.foregroundDomainMarkers.get(key);
            
if (markers != null) {
                
Iterator iterator = markers.iterator();
                
while (iterator.hasNext()) {
                    
Marker m = (Marker) iterator.next();
                    
m.removeChangeListener(this);
                
}
                
markers.clear();
            
}
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Removes a marker for the domain axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param marker
  
the marker.
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*/

    
public boolean removeDomainMarker(Marker marker) {
        
return removeDomainMarker(marker, Layer.FOREGROUND);
    
}

    
/**
     
* Removes a marker for the domain axis in the specified layer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param marker the marker ({@code null} not permitted).
     
* @param layer the layer (foreground or background).
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*/

    
public boolean removeDomainMarker(Marker marker, Layer layer) {
        
return removeDomainMarker(0, marker, layer);
    
}

    
/**
     
* Removes a marker for a specific dataset/renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index the dataset/renderer index.
     
* @param marker the marker.
     
* @param layer the layer (foreground or background).
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*/

    
public boolean removeDomainMarker(int index, Marker marker, Layer layer) {
        
return removeDomainMarker(index, marker, layer, true);
    
}

    
/**
     
* Removes a marker for a specific dataset/renderer and, if requested,
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index the dataset/renderer index.
     
* @param marker the marker.
     
* @param layer the layer (foreground or background).
     
* @param notify
  
notify listeners?
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.10
     
*/

    
public boolean removeDomainMarker(int index, Marker marker, Layer layer,
            
boolean notify) {
        
ArrayList markers;
        
if (layer == Layer.FOREGROUND) {
            
markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer(
                    
index));
        
} else {
            
markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer(
                    
index));
        
}
        
if (markers == null) {
            
return false;
        
}
        
boolean removed = markers.remove(marker);
        
if (removed && notify) {
            
fireChangeEvent();
        
}
        
return removed;
    
}

    
/**
     
* Adds a marker for display (in the foreground) against the range axis and
     
* sends a {@link PlotChangeEvent} to all registered listeners. Typically a
     
* marker will be drawn by the renderer as a line perpendicular to the
     
* range axis, however this is entirely up to the renderer.
     
*
     
* @param marker
  
the marker ({@code null} not permitted).
     
*
     
* @see #removeRangeMarker(Marker)
     
*/

    
public void addRangeMarker(Marker marker) {
        
addRangeMarker(marker, Layer.FOREGROUND);
    
}

    
/**
     
* Adds a marker for display against the range axis and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
  
Typically a marker
     
* will be drawn by the renderer as a line perpendicular to the range axis,
     
* however this is entirely up to the renderer.
     
*
     
* @param marker
  
the marker ({@code null} not permitted).
     
* @param layer
  
the layer (foreground or background) ({@code null}
     
*
               
not permitted).
     
*
     
* @see #removeRangeMarker(Marker, Layer)
     
*/

    
public void addRangeMarker(Marker marker, Layer layer) {
        
addRangeMarker(0, marker, layer);
    
}

    
/**
     
* Adds a marker for display by a particular renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
* <P>
     
* Typically a marker will be drawn by the renderer as a line perpendicular
     
* to a range axis, however this is entirely up to the renderer.
     
*
     
* @param index
  
the renderer index.
     
* @param marker
  
the marker.
     
* @param layer
  
the layer.
     
*
     
* @see #removeRangeMarker(int, Marker, Layer)
     
*/

    
public void addRangeMarker(int index, Marker marker, Layer layer) {
        
addRangeMarker(index, marker, layer, true);
    
}

    
/**
     
* Adds a marker for display by a particular renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
* <P>
     
* Typically a marker will be drawn by the renderer as a line perpendicular
     
* to a range axis, however this is entirely up to the renderer.
     
*
     
* @param index
  
the renderer index.
     
* @param marker
  
the marker.
     
* @param layer
  
the layer.
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.10
     
*
     
* @see #removeRangeMarker(int, Marker, Layer, boolean)
     
*/

    
public void addRangeMarker(int index, Marker marker, Layer layer,
            
boolean notify) {
        
Collection markers;
        
if (layer == Layer.FOREGROUND) {
            
markers = (Collection) this.foregroundRangeMarkers.get(
                    
new Integer(index));
            
if (markers == null) {
                
markers = new java.util.ArrayList();
                
this.foregroundRangeMarkers.put(new Integer(index), markers);
            
}
            
markers.add(marker);
        
} else if (layer == Layer.BACKGROUND) {
            
markers = (Collection) this.backgroundRangeMarkers.get(
                    
new Integer(index));
            
if (markers == null) {
                
markers = new java.util.ArrayList();
                
this.backgroundRangeMarkers.put(new Integer(index), markers);
            
}
            
markers.add(marker);
        
}
        
marker.addChangeListener(this);
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Clears all the range markers for the plot and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
*
 

     
*/

    
public void clearRangeMarkers() {
        
if (this.backgroundRangeMarkers != null) {
            
Set keys = this.backgroundRangeMarkers.keySet();
            
Iterator iterator = keys.iterator();
            
while (iterator.hasNext()) {
                
Integer key = (Integer) iterator.next();
                
clearRangeMarkers(key.intValue());
            
}
            
this.backgroundRangeMarkers.clear();
        
}
        
if (this.foregroundRangeMarkers != null) {
            
Set keys = this.foregroundRangeMarkers.keySet();
            
Iterator iterator = keys.iterator();
            
while (iterator.hasNext()) {
                
Integer key = (Integer) iterator.next();
                
clearRangeMarkers(key.intValue());
            
}
            
this.foregroundRangeMarkers.clear();
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Returns the list of range markers (read only) for the specified layer.
     
*
     
* @param layer
  
the layer (foreground or background).
     
*
     
* @return The list of range markers.
     
*
     
* @see #getRangeMarkers(int, Layer)
     
*/

    
public Collection getRangeMarkers(Layer layer) {
        
return getRangeMarkers(0, layer);
    
}

    
/**
     
* Returns a collection of range markers for a particular renderer and
     
* layer.
     
*
     
* @param index
  
the renderer index.
     
* @param layer
  
the layer.
     
*
     
* @return A collection of markers (possibly {@code null}).
     
*/

    
public Collection getRangeMarkers(int index, Layer layer) {
        
Collection result = null;
        
Integer key = new Integer(index);
        
if (layer == Layer.FOREGROUND) {
            
result = (Collection) this.foregroundRangeMarkers.get(key);
        
}
        
else if (layer == Layer.BACKGROUND) {
            
result = (Collection) this.backgroundRangeMarkers.get(key);
        
}
        
if (result != null) {
            
result = Collections.unmodifiableCollection(result);
        
}
        
return result;
    
}

    
/**
     
* Clears all the range markers for the specified renderer.
     
*
     
* @param index
  
the renderer index.
     
*
     
* @see #clearDomainMarkers(int)
     
*/

    
public void clearRangeMarkers(int index) {
        
Integer key = new Integer(index);
        
if (this.backgroundRangeMarkers != null) {
            
Collection markers
                
= (Collection) this.backgroundRangeMarkers.get(key);
            
if (markers != null) {
                
Iterator iterator = markers.iterator();
                
while (iterator.hasNext()) {
                    
Marker m = (Marker) iterator.next();
                    
m.removeChangeListener(this);
                
}
                
markers.clear();
            
}
        
}
        
if (this.foregroundRangeMarkers != null) {
            
Collection markers
                
= (Collection) this.foregroundRangeMarkers.get(key);
            
if (markers != null) {
                
Iterator iterator = markers.iterator();
                
while (iterator.hasNext()) {
                    
Marker m = (Marker) iterator.next();
                    
m.removeChangeListener(this);
                
}
                
markers.clear();
            
}
        
}
        
fireChangeEvent();
    
}

    
/**
     
* Removes a marker for the range axis and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param marker the marker.
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*
     
* @see #addRangeMarker(Marker)
     
*/

    
public boolean removeRangeMarker(Marker marker) {
        
return removeRangeMarker(marker, Layer.FOREGROUND);
    
}

    
/**
     
* Removes a marker for the range axis in the specified layer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param marker the marker ({@code null} not permitted).
     
* @param layer the layer (foreground or background).
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*
     
* @see #addRangeMarker(Marker, Layer)
     
*/

    
public boolean removeRangeMarker(Marker marker, Layer layer) {
        
return removeRangeMarker(0, marker, layer);
    
}

    
/**
     
* Removes a marker for a specific dataset/renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index the dataset/renderer index.
     
* @param marker the marker.
     
* @param layer the layer (foreground or background).
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.7
     
*
     
* @see #addRangeMarker(int, Marker, Layer)
     
*/

    
public boolean removeRangeMarker(int index, Marker marker, Layer layer) {
        
return removeRangeMarker(index, marker, layer, true);
    
}

    
/**
     
* Removes a marker for a specific dataset/renderer and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index
  
the dataset/renderer index.
     
* @param marker
  
the marker.
     
* @param layer
  
the layer (foreground or background).
     
* @param notify
  
notify listeners.
     
*
     
* @return A boolean indicating whether or not the marker was actually
     
*
         
removed.
     
*
     
* @since 1.0.10
     
*
     
* @see #addRangeMarker(int, Marker, Layer, boolean)
     
*/

    
public boolean removeRangeMarker(int index, Marker marker, Layer layer,
            
boolean notify) {
        
Args.nullNotPermitted(marker, "marker");
        
ArrayList markers;
        
if (layer == Layer.FOREGROUND) {
            
markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer(
                    
index));
        
} else {
            
markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer(
                    
index));
        
}
        
if (markers == null) {
            
return false;
        
}
        
boolean removed = markers.remove(marker);
        
if (removed && notify) {
            
fireChangeEvent();
        
}
        
return removed;
    
}

    
/**
     
* Returns the flag that controls whether or not the domain crosshair is
     
* displayed by the plot.
     
*
     
* @return A boolean.
     
*
     
* @since 1.0.11
     
*
     
* @see #setDomainCrosshairVisible(boolean)
     
*/

    
public boolean isDomainCrosshairVisible() {
        
return this.domainCrosshairVisible;
    
}

    
/**
     
* Sets the flag that controls whether or not the domain crosshair is
     
* displayed by the plot, and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*
     
* @param flag
  
the new flag value.
     
*
     
* @since 1.0.11
     
*
     
*
 

     
* @see #setRangeCrosshairVisible(boolean)
     
*/

    
public void setDomainCrosshairVisible(boolean flag) {
        
if (this.domainCrosshairVisible != flag) {
            
this.domainCrosshairVisible = flag;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the row key for the domain crosshair.
     
*
     
* @return The row key.
     
*
     
* @since 1.0.11
     
*/

    
public Comparable getDomainCrosshairRowKey() {
        
return this.domainCrosshairRowKey;
    
}

    
/**
     
* Sets the row key for the domain crosshair and sends a
     
* {PlotChangeEvent} to all registered listeners.
     
*
     
* @param key
  
the key.
     
*
     
* @since 1.0.11
     
*/

    
public void setDomainCrosshairRowKey(Comparable key) {
        
setDomainCrosshairRowKey(key, true);
    
}

    
/**
     
* Sets the row key for the domain crosshair and, if requested, sends a
     
* {PlotChangeEvent} to all registered listeners.
     
*
     
* @param key
  
the key.
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.11
     
*/

    
public void setDomainCrosshairRowKey(Comparable key, boolean notify) {
        
this.domainCrosshairRowKey = key;
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the column key for the domain crosshair.
     
*
     
* @return The column key.
     
*
     
* @since 1.0.11
     
*/

    
public Comparable getDomainCrosshairColumnKey() {
        
return this.domainCrosshairColumnKey;
    
}

    
/**
     
* Sets the column key for the domain crosshair and sends
     
* a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param key
  
the key.
     
*
     
* @since 1.0.11
     
*/

    
public void setDomainCrosshairColumnKey(Comparable key) {
        
setDomainCrosshairColumnKey(key, true);
    
}

    
/**
     
* Sets the column key for the domain crosshair and, if requested, sends
     
* a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param key
  
the key.
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.11
     
*/

    
public void setDomainCrosshairColumnKey(Comparable key, boolean notify) {
        
this.domainCrosshairColumnKey = key;
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the dataset index for the crosshair.
     
*
     
* @return The dataset index.
     
*
     
* @since 1.0.11
     
*/

    
public int getCrosshairDatasetIndex() {
        
return this.crosshairDatasetIndex;
    
}

    
/**
     
* Sets the dataset index for the crosshair and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index
  
the index.
     
*
     
* @since 1.0.11
     
*/

    
public void setCrosshairDatasetIndex(int index) {
        
setCrosshairDatasetIndex(index, true);
    
}

    
/**
     
* Sets the dataset index for the crosshair and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param index
  
the index.
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.11
     
*/

    
public void setCrosshairDatasetIndex(int index, boolean notify) {
        
this.crosshairDatasetIndex = index;
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the paint used to draw the domain crosshair.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @since 1.0.11
     
*
     
* @see #setDomainCrosshairPaint(Paint)
     
*
 

     
*/

    
public Paint getDomainCrosshairPaint() {
        
return this.domainCrosshairPaint;
    
}

    
/**
     
* Sets the paint used to draw the domain crosshair.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
* @since 1.0.11
     
*
     
*
 

     
*/

    
public void setDomainCrosshairPaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.domainCrosshairPaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the stroke used to draw the domain crosshair.
     
*
     
* @return The stroke (never {@code null}).
     
*
     
* @since 1.0.11
     
*
     
* @see #setDomainCrosshairStroke(Stroke)
     
*
 

     
*/

    
public Stroke getDomainCrosshairStroke() {
        
return this.domainCrosshairStroke;
    
}

    
/**
     
* Sets the stroke used to draw the domain crosshair, and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param stroke
  
the stroke ({@code null} not permitted).
     
*
     
* @since 1.0.11
     
*
     
*
 

     
*/

    
public void setDomainCrosshairStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.domainCrosshairStroke = stroke;
    
}

    
/**
     
* Returns a flag indicating whether or not the range crosshair is visible.
     
*
     
* @return The flag.
     
*
     
* @see #setRangeCrosshairVisible(boolean)
     
*/

    
public boolean isRangeCrosshairVisible() {
        
return this.rangeCrosshairVisible;
    
}

    
/**
     
* Sets the flag indicating whether or not the range crosshair is visible.
     
*
     
* @param flag
  
the new value of the flag.
     
*
     
*
 

     
*/

    
public void setRangeCrosshairVisible(boolean flag) {
        
if (this.rangeCrosshairVisible != flag) {
            
this.rangeCrosshairVisible = flag;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns a flag indicating whether or not the crosshair should "lock-on"
     
* to actual data values.
     
*
     
* @return The flag.
     
*
     
* @see #setRangeCrosshairLockedOnData(boolean)
     
*/

    
public boolean isRangeCrosshairLockedOnData() {
        
return this.rangeCrosshairLockedOnData;
    
}

    
/**
     
* Sets the flag indicating whether or not the range crosshair should
     
* "lock-on" to actual data values, and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param flag
  
the flag.
     
*
     
*
 

     
*/

    
public void setRangeCrosshairLockedOnData(boolean flag) {
        
if (this.rangeCrosshairLockedOnData != flag) {
            
this.rangeCrosshairLockedOnData = flag;
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the range crosshair value.
     
*
     
* @return The value.
     
*
     
* @see #setRangeCrosshairValue(double)
     
*/

    
public double getRangeCrosshairValue() {
        
return this.rangeCrosshairValue;
    
}

    
/**
     
* Sets the range crosshair value and, if the crosshair is visible, sends
     
* a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param value
  
the new value.
     
*
     
*
 

     
*/

    
public void setRangeCrosshairValue(double value) {
        
setRangeCrosshairValue(value, true);
    
}

    
/**
     
* Sets the range crosshair value and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners (but only if the
     
* crosshair is visible).
     
*
     
* @param value
  
the new value.
     
* @param notify
  
a flag that controls whether or not listeners are
     
*
                
notified.
     
*
     
*
 

     
*/

    
public void setRangeCrosshairValue(double value, boolean notify) {
        
this.rangeCrosshairValue = value;
        
if (isRangeCrosshairVisible() && notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Returns the pen-style ({@code Stroke}) used to draw the crosshair
     
* (if visible).
     
*
     
* @return The crosshair stroke (never {@code null}).
     
*
     
* @see #setRangeCrosshairStroke(Stroke)
     
*
 

     
*
 

     
*/

    
public Stroke getRangeCrosshairStroke() {
        
return this.rangeCrosshairStroke;
    
}

    
/**
     
* Sets the pen-style ({@code Stroke}) used to draw the range
     
* crosshair (if visible), and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*
     
* @param stroke
  
the new crosshair stroke ({@code null} not
     
*
         
permitted).
     
*
     
*
 

     
*/

    
public void setRangeCrosshairStroke(Stroke stroke) {
        
Args.nullNotPermitted(stroke, "stroke");
        
this.rangeCrosshairStroke = stroke;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the paint used to draw the range crosshair.
     
*
     
* @return The paint (never {@code null}).
     
*
     
* @see #setRangeCrosshairPaint(Paint)
     
*
 

     
*
 

     
*/

    
public Paint getRangeCrosshairPaint() {
        
return this.rangeCrosshairPaint;
    
}

    
/**
     
* Sets the paint used to draw the range crosshair (if visible) and
     
* sends a {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param paint
  
the paint ({@code null} not permitted).
     
*
     
*
 

     
*/

    
public void setRangeCrosshairPaint(Paint paint) {
        
Args.nullNotPermitted(paint, "paint");
        
this.rangeCrosshairPaint = paint;
        
fireChangeEvent();
    
}

    
/**
     
* Returns the list of annotations.
     
*
     
* @return The list of annotations (never {@code null}).
     
*
     
* @see #addAnnotation(CategoryAnnotation)
     
*
 

     
*/

    
public List getAnnotations() {
        
return this.annotations;
    
}

    
/**
     
* Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*
     
* @param annotation
  
the annotation ({@code null} not permitted).
     
*
     
* @see #removeAnnotation(CategoryAnnotation)
     
*/

    
public void addAnnotation(CategoryAnnotation annotation) {
        
addAnnotation(annotation, true);
    
}

    
/**
     
* Adds an annotation to the plot and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param annotation
  
the annotation ({@code null} not permitted).
     
* @param notify
  
notify listeners?
     
*
     
* @since 1.0.10
     
*/

    
public void addAnnotation(CategoryAnnotation annotation, boolean notify) {
        
Args.nullNotPermitted(annotation, "annotation");
        
this.annotations.add(annotation);
        
annotation.addChangeListener(this);
        
if (notify) {
            
fireChangeEvent();
        
}
    
}

    
/**
     
* Removes an annotation from the plot and sends a {@link PlotChangeEvent}
     
* to all registered listeners.
     
*
     
* @param annotation
  
the annotation ({@code null} not permitted).
     
*
     
* @return A boolean (indicates whether or not the annotation was removed).
     
*
     
* @see #addAnnotation(CategoryAnnotation)
     
*/

    
public boolean removeAnnotation(CategoryAnnotation annotation) {
        
return removeAnnotation(annotation, true);
    
}

    
/**
     
* Removes an annotation from the plot and, if requested, sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param annotation
  
the annotation ({@code null} not permitted).
     
* @param notify
  
notify listeners?
     
*
     
* @return A boolean (indicates whether or not the annotation was removed).
     
*
     
* @since 1.0.10
     
*/

    
public boolean removeAnnotation(CategoryAnnotation annotation,
            
boolean notify) {
        
Args.nullNotPermitted(annotation, "annotation");
        
boolean removed = this.annotations.remove(annotation);
        
annotation.removeChangeListener(this);
        
if (removed && notify) {
            
fireChangeEvent();
        
}
        
return removed;
    
}

    
/**
     
* Clears all the annotations and sends a {@link PlotChangeEvent} to all
     
* registered listeners.
     
*/

    
public void clearAnnotations() {
        
for (int i = 0; i < this.annotations.size(); i++) {
            
CategoryAnnotation annotation
                    
= (CategoryAnnotation) this.annotations.get(i);
            
annotation.removeChangeListener(this);
        
}
        
this.annotations.clear();
        
fireChangeEvent();
    
}

    
/**
     
* Returns the shadow generator for the plot, if any.
     
*
     
* @return The shadow generator (possibly {@code null}).
     
*
     
* @since 1.0.14
     
*/

    
public ShadowGenerator getShadowGenerator() {
        
return this.shadowGenerator;
    
}

    
/**
     
* Sets the shadow generator for the plot and sends a
     
* {@link PlotChangeEvent} to all registered listeners.
     
*
     
* @param generator
  
the generator ({@code null} permitted).
     
*
     
* @since 1.0.14
     
*/

    
public void setShadowGenerator(ShadowGenerator generator) {
        
this.shadowGenerator = generator;
        
fireChangeEvent();
    
}

    
/**
     
* Calculates the space required for the domain axis/axes.
     
*
     
* @param g2
  
the graphics device.
     
* @param plotArea
  
the plot area.
     
* @param space
  
a carrier for the result ({@code null} permitted).
     
*
     
* @return The required space.
     
*/

    
protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
            
Rectangle2D plotArea, AxisSpace space) {

        
if (space == null) {
            
space = new AxisSpace();
        
}

        
// reserve some space for the domain axis...
        
if (this.fixedDomainAxisSpace != null) {
            
if (this.orientation.isHorizontal()) {
                
space.ensureAtLeast(
                    
this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT);
                
space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(),
                        
RectangleEdge.RIGHT);
            
} else if (this.orientation.isVertical()) {
                
space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(),
                        
RectangleEdge.TOP);
                
space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(),
                        
RectangleEdge.BOTTOM);
            
}
        
}
        
else {
            
// reserve space for the primary domain axis...
            
RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
                    
getDomainAxisLocation(), this.orientation);
            
if (this.drawSharedDomainAxis) {
                
space = getDomainAxis().reserveSpace(g2, this, plotArea,
                        
domainEdge, space);
            
}

            
// reserve space for any domain axes...
            
for (CategoryAxis xAxis : this.domainAxes.values()) {
                
if (xAxis != null) {
                    
int i = getDomainAxisIndex(xAxis);
                    
RectangleEdge edge = getDomainAxisEdge(i);
                    
space = xAxis.reserveSpace(g2, this, plotArea, edge, space);
                
}
            
}
        
}

        
return space;

    
}

    
/**
     
* Calculates the space required for the range axis/axes.
     
*
     
* @param g2
  
the graphics device.
     
* @param plotArea
  
the plot area.
     
* @param space
  
a carrier for the result ({@code null} permitted).
     
*
     
* @return The required space.
     
*/

    
protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
            
Rectangle2D plotArea, AxisSpace space) {

        
if (space == null) {
            
space = new AxisSpace();
        
}

        
// reserve some space for the range axis...
        
if (this.fixedRangeAxisSpace != null) {
            
if (this.orientation.isHorizontal()) {
                
space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(),
                        
RectangleEdge.TOP);
                
space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(),
                        
RectangleEdge.BOTTOM);
            
} else if (this.orientation == PlotOrientation.VERTICAL) {
                
space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(),
                        
RectangleEdge.LEFT);
                
space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(),
                        
RectangleEdge.RIGHT);
            
}
        
} else {
            
// reserve space for the range axes (if any)...
            
for (ValueAxis yAxis : this.rangeAxes.values()) {
                
if (yAxis != null) {
                    
int i = findRangeAxisIndex(yAxis);
                    
RectangleEdge edge = getRangeAxisEdge(i);
                    
space = yAxis.reserveSpace(g2, this, plotArea, edge, space);
                
}
            
}
        
}
        
return space;

    
}

    
/**
     
* Trims a rectangle to integer coordinates.
     
*
     
* @param rect
  
the incoming rectangle.
     
*
     
* @return A rectangle with integer coordinates.
     
*/

    
private Rectangle integerise(Rectangle2D rect) {
        
int x0 = (int) Math.ceil(rect.getMinX());
        
int y0 = (int) Math.ceil(rect.getMinY());
        
int x1 = (int) Math.floor(rect.getMaxX());
        
int y1 = (int) Math.floor(rect.getMaxY());
        
return new Rectangle(x0, y0, (x1 - x0), (y1 - y0));
    
}

    
/**
     
* Calculates the space required for the axes.
     
*
     
* @param g2
  
the graphics device.
     
* @param plotArea
  
the plot area.
     
*
     
* @return The space required for the axes.
     
*/

    
protected AxisSpace calculateAxisSpace(Graphics2D g2,
            
Rectangle2D plotArea) {
        
AxisSpace space = new AxisSpace();
        
space = calculateRangeAxisSpace(g2, plotArea, space);
        
space = calculateDomainAxisSpace(g2, plotArea, space);
        
return space;
    
}

    
/**
     
* Draws the plot on a Java 2D graphics device (such as the screen or a
     
* printer).
     
* <P>
     
* At your option, you may supply an instance of {@link PlotRenderingInfo}.
     
* If you do, it will be populated with information about the drawing,
     
* including various plot dimensions and tooltip info.
     
*
     
* @param g2
  
the graphics device.
     
* @param area