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