    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2008, 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     * AbstractXYItemRenderer.java
    029     * ---------------------------
    030     * (C) Copyright 2002-2008, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Richard Atkinson;
    034     *                   Focus Computer Services Limited;
    035     *                   Tim Bardzil;
    036     *                   Sergei Ivanov;
    037     *
    038     * Changes:
    039     * --------
    040     * 15-Mar-2002 : Version 1 (DG);
    041     * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in
    042     *               the XYItemRenderer interface (DG);
    043     * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image
    044     *               maps (RA);
    045     * 20-Aug-2002 : Added property change events for the tooltip and URL
    046     *               generators (DG);
    047     * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG);
    048     * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG);
    049     * 18-Nov-2002 : Added methods for drawing grid lines (DG);
    050     * 17-Jan-2003 : Moved plot classes into a separate package (DG);
    051     * 25-Mar-2003 : Implemented Serializable (DG);
    052     * 01-May-2003 : Modified initialise() return type and drawItem() method
    053     *               signature (DG);
    054     * 15-May-2003 : Modified to take into account the plot orientation (DG);
    055     * 21-May-2003 : Added labels to markers (DG);
    056     * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
    057     *               Services Ltd) (DG);
    058     * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA);
    059     * 31-Jul-2003 : Deprecated all but the default constructor (DG);
    060     * 13-Aug-2003 : Implemented Cloneable (DG);
    061     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
    062     * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
    063     * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
    064     * 11-Feb-2004 : Updated labelling for markers (DG);
    065     * 25-Feb-2004 : Added updateCrosshairValues() method.  Moved deprecated code
    066     *               to bottom of source file (DG);
    067     * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method
    068     *               - thanks to Tim Bardzil (DG);
    069     * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis
    070     *               range (DG);
    071     * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG);
    072     * 26-Aug-2004 : Added the addEntity() method (DG);
    073     * 29-Sep-2004 : Added annotation support (with layers) (DG);
    074     * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
    075     *               TextUtilities (DG);
    076     * 06-Oct-2004 : Added findDomainBounds() method and renamed
    077     *               getRangeExtent() --> findRangeBounds() (DG);
    078     * 07-Jan-2005 : Removed deprecated code (DG);
    079     * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG);
    080     * 24-Feb-2005 : Added getLegendItems() method (DG);
    081     * 08-Mar-2005 : Fixed positioning of marker labels (DG);
    082     * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and
    083     *               added generators for legend labels, tooltips and URLs (DG);
    084     * 01-Jun-2005 : Handle one dimension of the marker label adjustment
    085     *               automatically (DG);
    086     * ------------- JFREECHART 1.0.x ---------------------------------------------
    087     * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
    088     * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei
    089     *               Ivanov) (DG);
    090     * 24-Oct-2006 : Added code to draw outlines for interval markers (DG);
    091     * 24-Nov-2006 : Fixed cloning for legend item generators (DG);
    092     * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into
    093     *               account multiple axis plots (see bug 1086307) (DG);
    094     * 20-Feb-2007 : Fixed equals() method implementation (DG);
    095     * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to
    096     *               Sergei Ivanov) (DG);
    097     * 22-Mar-2007 : Modified the tool tip generator look up (DG);
    098     * 23-Mar-2007 : Added drawDomainLine() method (DG);
    099     * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
    100     *               itemLabelGenerator and toolTipGenerator override fields (DG);
    101     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
    102     * 12-Nov-2007 : Fixed domain and range band drawing methods (DG);
    103     * 07-Apr-2008 : Minor API doc update (DG);
    104     * 14-May-2008 : Updated addEntity() method to take plot orientation into
    105     *               account when the incoming area is null (DG);
    106     * 02-Jun-2008 : Added isPointInRect() method (DG);
    107     *
    108     */
    109
    110    package org.jfree.chart.renderer.xy;
    111
    112    import java.awt.AlphaComposite;
    113    import java.awt.Composite;
    114    import java.awt.Font;
    115    import java.awt.GradientPaint;
    116    import java.awt.Graphics2D;
    117    import java.awt.Paint;
    118    import java.awt.Shape;
    119    import java.awt.Stroke;
    120    import java.awt.geom.Ellipse2D;
    121    import java.awt.geom.Line2D;
    122    import java.awt.geom.Point2D;
    123    import java.awt.geom.Rectangle2D;
    124    import java.io.Serializable;
    125    import java.util.Iterator;
    126    import java.util.List;
    127
    128    import org.jfree.chart.LegendItem;
    129    import org.jfree.chart.LegendItemCollection;
    130    import org.jfree.chart.annotations.XYAnnotation;
    131    import org.jfree.chart.axis.ValueAxis;
    132    import org.jfree.chart.entity.EntityCollection;
    133    import org.jfree.chart.entity.XYItemEntity;
    134    import org.jfree.chart.event.RendererChangeEvent;
    135    import org.jfree.chart.labels.ItemLabelPosition;
    136    import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
    137    import org.jfree.chart.labels.XYItemLabelGenerator;
    138    import org.jfree.chart.labels.XYSeriesLabelGenerator;
    139    import org.jfree.chart.labels.XYToolTipGenerator;
    140    import org.jfree.chart.plot.CrosshairState;
    141    import org.jfree.chart.plot.DrawingSupplier;
    142    import org.jfree.chart.plot.IntervalMarker;
    143    import org.jfree.chart.plot.Marker;
    144    import org.jfree.chart.plot.Plot;
    145    import org.jfree.chart.plot.PlotOrientation;
    146    import org.jfree.chart.plot.PlotRenderingInfo;
    147    import org.jfree.chart.plot.ValueMarker;
    148    import org.jfree.chart.plot.XYPlot;
    149    import org.jfree.chart.renderer.AbstractRenderer;
    150    import org.jfree.chart.urls.XYURLGenerator;
    151    import org.jfree.data.Range;
    152    import org.jfree.data.general.DatasetUtilities;
    153    import org.jfree.data.xy.XYDataset;
    154    import org.jfree.text.TextUtilities;
    155    import org.jfree.ui.GradientPaintTransformer;
    156    import org.jfree.ui.Layer;
    157    import org.jfree.ui.LengthAdjustmentType;
    158    import org.jfree.ui.RectangleAnchor;
    159    import org.jfree.ui.RectangleInsets;
    160    import org.jfree.util.ObjectList;
    161    import org.jfree.util.ObjectUtilities;
    162    import org.jfree.util.PublicCloneable;
    163
    164    /**
    165     * A base class that can be used to create new {@link XYItemRenderer}
    166     * implementations.
    167     */
    168    public abstract class AbstractXYItemRenderer extends AbstractRenderer
    169            implements XYItemRenderer, Cloneable, Serializable {
    170
    171        /** For serialization. */
    172        private static final long serialVersionUID = 8019124836026607990L;
    173
    174        /** The plot. */
    175        private XYPlot plot;
    176
    177        /**
    178         * The item label generator for ALL series.
    179         *
    180         * @deprecated This field is redundant, use itemLabelGeneratorList and
    181         *     baseItemLabelGenerator instead.  Deprecated as of version 1.0.6.
    182         */
    183        private XYItemLabelGenerator itemLabelGenerator;
    184
    185        /** A list of item label generators (one per series). */
    186        private ObjectList itemLabelGeneratorList;
    187
    188        /** The base item label generator. */
    189        private XYItemLabelGenerator baseItemLabelGenerator;
    190
    191        /**
    192         * The tool tip generator for ALL series.
    193         *
    194         * @deprecated This field is redundant, use tooltipGeneratorList and
    195         *     baseToolTipGenerator instead.  Deprecated as of version 1.0.6.
    196         */
    197        private XYToolTipGenerator toolTipGenerator;
    198
    199        /** A list of tool tip generators (one per series). */
    200        private ObjectList toolTipGeneratorList;
    201
    202        /** The base tool tip generator. */
    203        private XYToolTipGenerator baseToolTipGenerator;
    204
    205        /** The URL text generator. */
    206        private XYURLGenerator urlGenerator;
    207
    208        /**
    209         * Annotations to be drawn in the background layer ('underneath' the data
    210         * items).
    211         */
    212        private List backgroundAnnotations;
    213
    214        /**
    215         * Annotations to be drawn in the foreground layer ('on top' of the data
    216         * items).
    217         */
    218        private List foregroundAnnotations;
    219
    220        /** The default radius for the entity 'hotspot' */
    221        private int defaultEntityRadius;
    222
    223        /** The legend item label generator. */
    224        private XYSeriesLabelGenerator legendItemLabelGenerator;
    225
    226        /** The legend item tool tip generator. */
    227        private XYSeriesLabelGenerator legendItemToolTipGenerator;
    228
    229        /** The legend item URL generator. */
    230        private XYSeriesLabelGenerator legendItemURLGenerator;
    231
    232        /**
    233         * Creates a renderer where the tooltip generator and the URL generator are
    234         * both <code>null</code>.
    235         */
    236        protected AbstractXYItemRenderer() {
    237            super();
    238            this.itemLabelGenerator = null;
    239            this.itemLabelGeneratorList = new ObjectList();
    240            this.toolTipGenerator = null;
    241            this.toolTipGeneratorList = new ObjectList();
    242            this.urlGenerator = null;
    243            this.backgroundAnnotations = new java.util.ArrayList();
    244            this.foregroundAnnotations = new java.util.ArrayList();
    245            this.defaultEntityRadius = 3;
    246            this.legendItemLabelGenerator = new StandardXYSeriesLabelGenerator(
    247                    "{0}");
    248        }
    249
    250        /**
    251         * Returns the number of passes through the data that the renderer requires
    252         * in order to draw the chart.  Most charts will require a single pass, but
    253         * some require two passes.
    254         *
    255         * @return The pass count.
    256         */
    257        public int getPassCount() {
    258            return 1;
    259        }
    260
    261        /**
    262         * Returns the plot that the renderer is assigned to.
    263         *
    264         * @return The plot (possibly <code>null</code>).
    265         */
    266        public XYPlot getPlot() {
    267            return this.plot;
    268        }
    269
    270        /**
    271         * Sets the plot that the renderer is assigned to.
    272         *
    273         * @param plot  the plot (<code>null</code> permitted).
    274         */
    275        public void setPlot(XYPlot plot) {
    276            this.plot = plot;
    277        }
    278
    279        /**
    280         * Initialises the renderer and returns a state object that should be
    281         * passed to all subsequent calls to the drawItem() method.
    282         * <P>
    283         * This method will be called before the first item is rendered, giving the
    284         * renderer an opportunity to initialise any state information it wants to
    285         * maintain.  The renderer can do nothing if it chooses.
    286         *
    287         * @param g2  the graphics device.
    288         * @param dataArea  the area inside the axes.
    289         * @param plot  the plot.
    290         * @param data  the data.
    291         * @param info  an optional info collection object to return data back to
    292         *              the caller.
    293         *
    294         * @return The renderer state (never <code>null</code>).
    295         */
    296        public XYItemRendererState initialise(Graphics2D g2,
    297                                              Rectangle2D dataArea,
    298                                              XYPlot plot,
    299                                              XYDataset data,
    300                                              PlotRenderingInfo info) {
    301
    302            XYItemRendererState state = new XYItemRendererState(info);
    303            return state;
    304
    305        }
    306
    307        // ITEM LABEL GENERATOR
    308
    309        /**
    310         * Returns the label generator for a data item.  This implementation simply
    311         * passes control to the {@link #getSeriesItemLabelGenerator(int)} method.
    312         * If, for some reason, you want a different generator for individual
    313         * items, you can override this method.
    314         *
    315         * @param series  the series index (zero based).
    316         * @param item  the item index (zero based).
    317         *
    318         * @return The generator (possibly <code>null</code>).
    319         */
    320        public XYItemLabelGenerator getItemLabelGenerator(int series, int item) {
    321            // return the generator for ALL series, if there is one...
    322            if (this.itemLabelGenerator != null) {
    323                return this.itemLabelGenerator;
    324            }
    325
    326            // otherwise look up the generator table
    327            XYItemLabelGenerator generator
    328                = (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
    329            if (generator == null) {
    330                generator = this.baseItemLabelGenerator;
    331            }
    332            return generator;
    333        }
    334
    335        /**
    336         * Returns the item label generator for a series.
    337         *
    338         * @param series  the series index (zero based).
    339         *
    340         * @return The generator (possibly <code>null</code>).
    341         */
    342        public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) {
    343            return (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
    344        }
    345
    346        /**
    347         * Returns the item label generator override.
    348         *
    349         * @return The generator (possibly <code>null</code>).
    350         *
    351         * @since 1.0.5
    352         *
    353         * @see #setItemLabelGenerator(XYItemLabelGenerator)
    354         *
    355         * @deprecated As of version 1.0.6, this override setting should not be
    356         *     used.  You can use the base setting instead
    357         *     ({@link #getBaseItemLabelGenerator()}).
    358         */
    359        public XYItemLabelGenerator getItemLabelGenerator() {
    360            return this.itemLabelGenerator;
    361        }
    362
    363        /**
    364         * Sets the item label generator for ALL series and sends a
    365         * {@link RendererChangeEvent} to all registered listeners.
    366         *
    367         * @param generator  the generator (<code>null</code> permitted).
    368         *
    369         * @see #getItemLabelGenerator()
    370         *
    371         * @deprecated As of version 1.0.6, this override setting should not be
    372         *     used.  You can use the base setting instead
    373         *     ({@link #setBaseItemLabelGenerator(XYItemLabelGenerator)}).
    374         */
    375        public void setItemLabelGenerator(XYItemLabelGenerator generator) {
    376            this.itemLabelGenerator = generator;
    377            fireChangeEvent();
    378        }
    379
    380        /**
    381         * Sets the item label generator for a series and sends a
    382         * {@link RendererChangeEvent} to all registered listeners.
    383         *
    384         * @param series  the series index (zero based).
    385         * @param generator  the generator (<code>null</code> permitted).
    386         */
    387        public void setSeriesItemLabelGenerator(int series,
    388                                                XYItemLabelGenerator generator) {
    389            this.itemLabelGeneratorList.set(series, generator);
    390            fireChangeEvent();
    391        }
    392
    393        /**
    394         * Returns the base item label generator.
    395         *
    396         * @return The generator (possibly <code>null</code>).
    397         */
    398        public XYItemLabelGenerator getBaseItemLabelGenerator() {
    399            return this.baseItemLabelGenerator;
    400        }
    401
    402        /**
    403         * Sets the base item label generator and sends a
    404         * {@link RendererChangeEvent} to all registered listeners.
    405         *
    406         * @param generator  the generator (<code>null</code> permitted).
    407         */
    408        public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) {
    409            this.baseItemLabelGenerator = generator;
    410            fireChangeEvent();
    411        }
    412
    413        // TOOL TIP GENERATOR
    414
    415        /**
    416         * Returns the tool tip generator for a data item.  If, for some reason,
    417         * you want a different generator for individual items, you can override
    418         * this method.
    419         *
    420         * @param series  the series index (zero based).
    421         * @param item  the item index (zero based).
    422         *
    423         * @return The generator (possibly <code>null</code>).
    424         */
    425        public XYToolTipGenerator getToolTipGenerator(int series, int item) {
    426            // return the generator for ALL series, if there is one...
    427            if (this.toolTipGenerator != null) {
    428                return this.toolTipGenerator;
    429            }
    430
    431            // otherwise look up the generator table
    432            XYToolTipGenerator generator
    433                    = (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
    434            if (generator == null) {
    435                generator = this.baseToolTipGenerator;
    436            }
    437            return generator;
    438        }
    439
    440        /**
    441         * Returns the override tool tip generator.
    442         *
    443         * @return The tool tip generator (possible <code>null</code>).
    444         *
    445         * @since 1.0.5
    446         *
    447         * @see #setToolTipGenerator(XYToolTipGenerator)
    448         *
    449         * @deprecated As of version 1.0.6, this override setting should not be
    450         *     used.  You can use the base setting instead
    451         *     ({@link #getBaseToolTipGenerator()}).
    452         */
    453        public XYToolTipGenerator getToolTipGenerator() {
    454            return this.toolTipGenerator;
    455        }
    456
    457        /**
    458         * Sets the tool tip generator for ALL series and sends a
    459         * {@link RendererChangeEvent} to all registered listeners.
    460         *
    461         * @param generator  the generator (<code>null</code> permitted).
    462         *
    463         * @see #getToolTipGenerator()
    464         *
    465         * @deprecated As of version 1.0.6, this override setting should not be
    466         *     used.  You can use the base setting instead
    467         *     ({@link #setBaseToolTipGenerator(XYToolTipGenerator)}).
    468         */
    469        public void setToolTipGenerator(XYToolTipGenerator generator) {
    470            this.toolTipGenerator = generator;
    471            fireChangeEvent();
    472        }
    473
    474        /**
    475         * Returns the tool tip generator for a series.
    476         *
    477         * @param series  the series index (zero based).
    478         *
    479         * @return The generator (possibly <code>null</code>).
    480         */
    481        public XYToolTipGenerator getSeriesToolTipGenerator(int series) {
    482            return (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
    483        }
    484
    485        /**
    486         * Sets the tool tip generator for a series and sends a
    487         * {@link RendererChangeEvent} to all registered listeners.
    488         *
    489         * @param series  the series index (zero based).
    490         * @param generator  the generator (<code>null</code> permitted).
    491         */
    492        public void setSeriesToolTipGenerator(int series,
    493                                              XYToolTipGenerator generator) {
    494            this.toolTipGeneratorList.set(series, generator);
    495            fireChangeEvent();
    496        }
    497
    498        /**
    499         * Returns the base tool tip generator.
    500         *
    501         * @return The generator (possibly <code>null</code>).
    502         *
    503         * @see #setBaseToolTipGenerator(XYToolTipGenerator)
    504         */
    505        public XYToolTipGenerator getBaseToolTipGenerator() {
    506            return this.baseToolTipGenerator;
    507        }
    508
    509        /**
    510         * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
    511         * to all registered listeners.
    512         *
    513         * @param generator  the generator (<code>null</code> permitted).
    514         *
    515         * @see #getBaseToolTipGenerator()
    516         */
    517        public void setBaseToolTipGenerator(XYToolTipGenerator generator) {
    518            this.baseToolTipGenerator = generator;
    519            fireChangeEvent();
    520        }
    521
    522        // URL GENERATOR
    523
    524        /**
    525         * Returns the URL generator for HTML image maps.
    526         *
    527         * @return The URL generator (possibly <code>null</code>).
    528         */
    529        public XYURLGenerator getURLGenerator() {
    530            return this.urlGenerator;
    531        }
    532
    533        /**
    534         * Sets the URL generator for HTML image maps and sends a
    535         * {@link RendererChangeEvent} to all registered listeners.
    536         *
    537         * @param urlGenerator  the URL generator (<code>null</code> permitted).
    538         */
    539        public void setURLGenerator(XYURLGenerator urlGenerator) {
    540            this.urlGenerator = urlGenerator;
    541            fireChangeEvent();
    542        }
    543
    544        /**
    545         * Adds an annotation and sends a {@link RendererChangeEvent} to all
    546         * registered listeners.  The annotation is added to the foreground
    547         * layer.
    548         *
    549         * @param annotation  the annotation (<code>null</code> not permitted).
    550         */
    551        public void addAnnotation(XYAnnotation annotation) {
    552            // defer argument checking
    553            addAnnotation(annotation, Layer.FOREGROUND);
    554        }
    555
    556        /**
    557         * Adds an annotation to the specified layer and sends a
    558         * {@link RendererChangeEvent} to all registered listeners.
    559         *
    560         * @param annotation  the annotation (<code>null</code> not permitted).
    561         * @param layer  the layer (<code>null</code> not permitted).
    562         */
    563        public void addAnnotation(XYAnnotation annotation, Layer layer) {
    564            if (annotation == null) {
    565                throw new IllegalArgumentException("Null 'annotation' argument.");
    566            }
    567            if (layer.equals(Layer.FOREGROUND)) {
    568                this.foregroundAnnotations.add(annotation);
    569                fireChangeEvent();
    570            }
    571            else if (layer.equals(Layer.BACKGROUND)) {
    572                this.backgroundAnnotations.add(annotation);
    573                fireChangeEvent();
    574            }
    575            else {
    576                // should never get here
    577                throw new RuntimeException("Unknown layer.");
    578            }
    579        }
    580        /**
    581         * Removes the specified annotation and sends a {@link RendererChangeEvent}
    582         * to all registered listeners.
    583         *
    584         * @param annotation  the annotation to remove (<code>null</code> not
    585         *                    permitted).
    586         *
    587         * @return A boolean to indicate whether or not the annotation was
    588         *         successfully removed.
    589         */
    590        public boolean removeAnnotation(XYAnnotation annotation) {
    591            boolean removed = this.foregroundAnnotations.remove(annotation);
    592            removed = removed & this.backgroundAnnotations.remove(annotation);
    593            fireChangeEvent();
    594            return removed;
    595        }
    596
    597        /**
    598         * Removes all annotations and sends a {@link RendererChangeEvent}
    599         * to all registered listeners.
    600         */
    601        public void removeAnnotations() {
    602            this.foregroundAnnotations.clear();
    603            this.backgroundAnnotations.clear();
    604            fireChangeEvent();
    605        }
    606
    607        /**
    608         * Returns the radius of the circle used for the default entity area
    609         * when no area is specified.
    610         *
    611         * @return A radius.
    612         */
    613        public int getDefaultEntityRadius() {
    614            return this.defaultEntityRadius;
    615        }
    616
    617        /**
    618         * Sets the radius of the circle used for the default entity area
    619         * when no area is specified.
    620         *
    621         * @param radius  the radius.
    622         */
    623        public void setDefaultEntityRadius(int radius) {
    624            this.defaultEntityRadius = radius;
    625        }
    626
    627        /**
    628         * Returns the legend item label generator.
    629         *
    630         * @return The label generator (never <code>null</code>).
    631         *
    632         * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator)
    633         */
    634        public XYSeriesLabelGenerator getLegendItemLabelGenerator() {
    635            return this.legendItemLabelGenerator;
    636        }
    637
    638        /**
    639         * Sets the legend item label generator and sends a
    640         * {@link RendererChangeEvent} to all registered listeners.
    641         *
    642         * @param generator  the generator (<code>null</code> not permitted).
    643         *
    644         * @see #getLegendItemLabelGenerator()
    645         */
    646        public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) {
    647            if (generator == null) {
    648                throw new IllegalArgumentException("Null 'generator' argument.");
    649            }
    650            this.legendItemLabelGenerator = generator;
    651            fireChangeEvent();
    652        }
    653
    654        /**
    655         * Returns the legend item tool tip generator.
    656         *
    657         * @return The tool tip generator (possibly <code>null</code>).
    658         *
    659         * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator)
    660         */
    661        public XYSeriesLabelGenerator getLegendItemToolTipGenerator() {
    662            return this.legendItemToolTipGenerator;
    663        }
    664
    665        /**
    666         * Sets the legend item tool tip generator and sends a
    667         * {@link RendererChangeEvent} to all registered listeners.
    668         *
    669         * @param generator  the generator (<code>null</code> permitted).
    670         *
    671         * @see #getLegendItemToolTipGenerator()
    672         */
    673        public void setLegendItemToolTipGenerator(
    674                XYSeriesLabelGenerator generator) {
    675            this.legendItemToolTipGenerator = generator;
    676            fireChangeEvent();
    677        }
    678
    679        /**
    680         * Returns the legend item URL generator.
    681         *
    682         * @return The URL generator (possibly <code>null</code>).
    683         *
    684         * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator)
    685         */
    686        public XYSeriesLabelGenerator getLegendItemURLGenerator() {
    687            return this.legendItemURLGenerator;
    688        }
    689
    690        /**
    691         * Sets the legend item URL generator and sends a
    692         * {@link RendererChangeEvent} to all registered listeners.
    693         *
    694         * @param generator  the generator (<code>null</code> permitted).
    695         *
    696         * @see #getLegendItemURLGenerator()
    697         */
    698        public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) {
    699            this.legendItemURLGenerator = generator;
    700            fireChangeEvent();
    701        }
    702
    703        /**
    704         * Returns the lower and upper bounds (range) of the x-values in the
    705         * specified dataset.
    706         *
    707         * @param dataset  the dataset (<code>null</code> permitted).
    708         *
    709         * @return The range (<code>null</code> if the dataset is <code>null</code>
    710         *         or empty).
    711         */
    712        public Range findDomainBounds(XYDataset dataset) {
    713            if (dataset != null) {
    714                return DatasetUtilities.findDomainBounds(dataset, false);
    715            }
    716            else {
    717                return null;
    718            }
    719        }
    720
    721        /**
    722         * Returns the range of values the renderer requires to display all the
    723         * items from the specified dataset.
    724         *
    725         * @param dataset  the dataset (<code>null</code> permitted).
    726         *
    727         * @return The range (<code>null</code> if the dataset is <code>null</code>
    728         *         or empty).
    729         */
    730        public Range findRangeBounds(XYDataset dataset) {
    731            if (dataset != null) {
    732                return DatasetUtilities.findRangeBounds(dataset, false);
    733            }
    734            else {
    735                return null;
    736            }
    737        }
    738
    739        /**
    740         * Returns a (possibly empty) collection of legend items for the series
    741         * that this renderer is responsible for drawing.
    742         *
    743         * @return The legend item collection (never <code>null</code>).
    744         */
    745        public LegendItemCollection getLegendItems() {
    746            if (this.plot == null) {
    747                return new LegendItemCollection();
    748            }
    749            LegendItemCollection result = new LegendItemCollection();
    750            int index = this.plot.getIndexOf(this);
    751            XYDataset dataset = this.plot.getDataset(index);
    752            if (dataset != null) {
    753                int seriesCount = dataset.getSeriesCount();
    754                for (int i = 0; i < seriesCount; i++) {
    755                    if (isSeriesVisibleInLegend(i)) {
    756                        LegendItem item = getLegendItem(index, i);
    757                        if (item != null) {
    758                            result.add(item);
    759                        }
    760                    }
    761                }
    762
    763            }
    764            return result;
    765        }
    766
    767        /**
    768         * Returns a default legend item for the specified series.  Subclasses
    769         * should override this method to generate customised items.
    770         *
    771         * @param datasetIndex  the dataset index (zero-based).
    772         * @param series  the series index (zero-based).
    773         *
    774         * @return A legend item for the series.
    775         */
    776        public LegendItem getLegendItem(int datasetIndex, int series) {
    777            LegendItem result = null;
    778            XYPlot xyplot = getPlot();
    779            if (xyplot != null) {
    780                XYDataset dataset = xyplot.getDataset(datasetIndex);
    781                if (dataset != null) {
    782                    String label = this.legendItemLabelGenerator.generateLabel(
    783                            dataset, series);
    784                    String description = label;
    785                    String toolTipText = null;
    786                    if (getLegendItemToolTipGenerator() != null) {
    787                        toolTipText = getLegendItemToolTipGenerator().generateLabel(
    788                                dataset, series);
    789                    }
    790                    String urlText = null;
    791                    if (getLegendItemURLGenerator() != null) {
    792                        urlText = getLegendItemURLGenerator().generateLabel(
    793                                dataset, series);
    794                    }
    795                    Shape shape = lookupSeriesShape(series);
    796                    Paint paint = lookupSeriesPaint(series);
    797                    Paint outlinePaint = lookupSeriesOutlinePaint(series);
    798                    Stroke outlineStroke = lookupSeriesOutlineStroke(series);
    799                    result = new LegendItem(label, description, toolTipText,
    800                            urlText, shape, paint, outlineStroke, outlinePaint);
    801                    result.setSeriesKey(dataset.getSeriesKey(series));
    802                    result.setSeriesIndex(series);
    803                    result.setDataset(dataset);
    804                    result.setDatasetIndex(datasetIndex);
    805                }
    806            }
    807            return result;
    808        }
    809
    810        /**
    811         * Fills a band between two values on the axis.  This can be used to color
    812         * bands between the grid lines.
    813         *
    814         * @param g2  the graphics device.
    815         * @param plot  the plot.
    816         * @param axis  the domain axis.
    817         * @param dataArea  the data area.
    818         * @param start  the start value.
    819         * @param end  the end value.
    820         */
    821        public void fillDomainGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
    822                Rectangle2D dataArea, double start, double end) {
    823
    824            double x1 = axis.valueToJava2D(start, dataArea,
    825                    plot.getDomainAxisEdge());
    826            double x2 = axis.valueToJava2D(end, dataArea,
    827                    plot.getDomainAxisEdge());
    828            Rectangle2D band;
    829            if (plot.getOrientation() == PlotOrientation.VERTICAL) {
    830                band = new Rectangle2D.Double(Math.min(x1, x2), dataArea.getMinY(),
    831                        Math.abs(x2 - x1), dataArea.getWidth());
    832            }
    833            else {
    834                band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(x1, x2),
    835                        dataArea.getWidth(), Math.abs(x2 - x1));
    836            }
    837            Paint paint = plot.getDomainTickBandPaint();
    838
    839            if (paint != null) {
    840                g2.setPaint(paint);
    841                g2.fill(band);
    842            }
    843
    844        }
    845
    846        /**
    847         * Fills a band between two values on the range axis.  This can be used to
    848         * color bands between the grid lines.
    849         *
    850         * @param g2  the graphics device.
    851         * @param plot  the plot.
    852         * @param axis  the range axis.
    853         * @param dataArea  the data area.
    854         * @param start  the start value.
    855         * @param end  the end value.
    856         */
    857        public void fillRangeGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
    858                Rectangle2D dataArea, double start, double end) {
    859
    860            double y1 = axis.valueToJava2D(start, dataArea,
    861                    plot.getRangeAxisEdge());
    862            double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge());
    863            Rectangle2D band;
    864            if (plot.getOrientation() == PlotOrientation.VERTICAL) {
    865                band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(y1, y2),
    866                    dataArea.getWidth(), Math.abs(y2 - y1));
    867            }
    868            else {
    869                band = new Rectangle2D.Double(Math.min(y1, y2), dataArea.getMinY(),
    870                        Math.abs(y2 - y1), dataArea.getHeight());
    871            }
    872            Paint paint = plot.getRangeTickBandPaint();
    873
    874            if (paint != null) {
    875                g2.setPaint(paint);
    876                g2.fill(band);
    877            }
    878
    879        }
    880
    881        /**
    882         * Draws a grid line against the range axis.
    883         *
    884         * @param g2  the graphics device.
    885         * @param plot  the plot.
    886         * @param axis  the value axis.
    887         * @param dataArea  the area for plotting data (not yet adjusted for any
    888         *                  3D effect).
    889         * @param value  the value at which the grid line should be drawn.
    890         */
    891        public void drawDomainGridLine(Graphics2D g2,
    892                                       XYPlot plot,
    893                                       ValueAxis axis,
    894                                       Rectangle2D dataArea,
    895                                       double value) {
    896
    897            Range range = axis.getRange();
    898            if (!range.contains(value)) {
    899                return;
    900            }
    901
    902            PlotOrientation orientation = plot.getOrientation();
    903            double v = axis.valueToJava2D(value, dataArea,
    904                    plot.getDomainAxisEdge());
    905            Line2D line = null;
    906            if (orientation == PlotOrientation.HORIZONTAL) {
    907                line = new Line2D.Double(dataArea.getMinX(), v,
    908                        dataArea.getMaxX(), v);
    909            }
    910            else if (orientation == PlotOrientation.VERTICAL) {
    911                line = new Line2D.Double(v, dataArea.getMinY(), v,
    912                        dataArea.getMaxY());
    913            }
    914
    915            Paint paint = plot.getDomainGridlinePaint();
    916            Stroke stroke = plot.getDomainGridlineStroke();
    917            g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
    918            g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
    919            g2.draw(line);
    920
    921        }
    922
    923        /**
    924         * Draws a line perpendicular to the domain axis.
    925         *
    926         * @param g2  the graphics device.
    927         * @param plot  the plot.
    928         * @param axis  the value axis.
    929         * @param dataArea  the area for plotting data (not yet adjusted for any 3D
    930         *                  effect).
    931         * @param value  the value at which the grid line should be drawn.
    932         * @param paint  the paint.
    933         * @param stroke  the stroke.
    934         *
    935         * @since 1.0.5
    936         */
    937        public void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis,
    938                Rectangle2D dataArea, double value, Paint paint, Stroke stroke) {
    939
    940            Range range = axis.getRange();
    941            if (!range.contains(value)) {
    942                return;
    943            }
    944
    945            PlotOrientation orientation = plot.getOrientation();
    946            Line2D line = null;
    947            double v = axis.valueToJava2D(value, dataArea,
    948                    plot.getDomainAxisEdge());
    949            if (orientation == PlotOrientation.HORIZONTAL) {
    950                line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(),
    951                        v);
    952            }
    953            else if (orientation == PlotOrientation.VERTICAL) {
    954                line = new Line2D.Double(v, dataArea.getMinY(), v,
    955                        dataArea.getMaxY());
    956            }
    957
    958            g2.setPaint(paint);
    959            g2.setStroke(stroke);
    960            g2.draw(line);
    961
    962        }
    963
    964        /**
    965         * Draws a line perpendicular to the range axis.
    966         *
    967         * @param g2  the graphics device.
    968         * @param plot  the plot.
    969         * @param axis  the value axis.
    970         * @param dataArea  the area for plotting data (not yet adjusted for any 3D
    971         *                  effect).
    972         * @param value  the value at which the grid line should be drawn.
    973         * @param paint  the paint.
    974         * @param stroke  the stroke.
    975         */
    976        public void drawRangeLine(Graphics2D g2,
    977                                  XYPlot plot,
    978                                  ValueAxis axis,
    979                                  Rectangle2D dataArea,
    980                                  double value,
    981                                  Paint paint,
    982                                  Stroke stroke) {
    983
    984            Range range = axis.getRange();
    985            if (!range.contains(value)) {
    986                return;
    987            }
    988
    989            PlotOrientation orientation = plot.getOrientation();
    990            Line2D line = null;
    991            double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge());
    992            if (orientation == PlotOrientation.HORIZONTAL) {
    993                line = new Line2D.Double(v, dataArea.getMinY(), v,
    994                        dataArea.getMaxY());
    995            }
    996            else if (orientation == PlotOrientation.VERTICAL) {
    997                line = new Line2D.Double(dataArea.getMinX(), v,
    998                        dataArea.getMaxX(), v);
    999            }
    1000
    1001            g2.setPaint(paint);
    1002            g2.setStroke(stroke);
    1003            g2.draw(line);
    1004
    1005        }
    1006
    1007        /**
    1008         * Draws a vertical line on the chart to represent a 'range marker'.
    1009         *
    1010         * @param g2  the graphics device.
    1011         * @param plot  the plot.
    1012         * @param domainAxis  the domain axis.
    1013         * @param marker  the marker line.
    1014         * @param dataArea  the axis data area.
    1015         */
    1016        public void drawDomainMarker(Graphics2D g2,
    1017                                     XYPlot plot,
    1018                                     ValueAxis domainAxis,
    1019                                     Marker marker,
    1020                                     Rectangle2D dataArea) {
    1021
    1022            if (marker instanceof ValueMarker) {
    1023                ValueMarker vm = (ValueMarker) marker;
    1024                double value = vm.getValue();
    1025                Range range = domainAxis.getRange();
    1026                if (!range.contains(value)) {
    1027                    return;
    1028                }
    1029
    1030                double v = domainAxis.valueToJava2D(value, dataArea,
    1031                        plot.getDomainAxisEdge());
    1032
    1033                PlotOrientation orientation = plot.getOrientation();
    1034                Line2D line = null;
    1035                if (orientation == PlotOrientation.HORIZONTAL) {
    1036                    line = new Line2D.Double(dataArea.getMinX(), v,
    1037                            dataArea.getMaxX(), v);
    1038                }
    1039                else if (orientation == PlotOrientation.VERTICAL) {
    1040                    line = new Line2D.Double(v, dataArea.getMinY(), v,
    1041                            dataArea.getMaxY());
    1042                }
    1043
    1044                final Composite originalComposite = g2.getComposite();
    1045                g2.setComposite(AlphaComposite.getInstance(
    1046                        AlphaComposite.SRC_OVER, marker.getAlpha()));
    1047                g2.setPaint(marker.getPaint());
    1048                g2.setStroke(marker.getStroke());
    1049                g2.draw(line);
    1050
    1051                String label = marker.getLabel();
    1052                RectangleAnchor anchor = marker.getLabelAnchor();
    1053                if (label != null) {
    1054                    Font labelFont = marker.getLabelFont();
    1055                    g2.setFont(labelFont);
    1056                    g2.setPaint(marker.getLabelPaint());
    1057                    Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
    1058                            g2, orientation, dataArea, line.getBounds2D(),
    1059                            marker.getLabelOffset(),
    1060                            LengthAdjustmentType.EXPAND, anchor);
    1061                    TextUtilities.drawAlignedString(label, g2,
    1062                            (float) coordinates.getX(), (float) coordinates.getY(),
    1063                            marker.getLabelTextAnchor());
    1064                }
    1065                g2.setComposite(originalComposite);
    1066            }
    1067            else if (marker instanceof IntervalMarker) {
    1068                IntervalMarker im = (IntervalMarker) marker;
    1069                double start = im.getStartValue();
    1070                double end = im.getEndValue();
    1071                Range range = domainAxis.getRange();
    1072                if (!(range.intersects(start, end))) {
    1073                    return;
    1074                }
    1075
    1076                double start2d = domainAxis.valueToJava2D(start, dataArea,
    1077                        plot.getDomainAxisEdge());
    1078                double end2d = domainAxis.valueToJava2D(end, dataArea,
    1079                        plot.getDomainAxisEdge());
    1080                double low = Math.min(start2d, end2d);
    1081                double high = Math.max(start2d, end2d);
    1082
    1083                PlotOrientation orientation = plot.getOrientation();
    1084                Rectangle2D rect = null;
    1085                if (orientation == PlotOrientation.HORIZONTAL) {
    1086                    // clip top and bottom bounds to data area
    1087                    low = Math.max(low, dataArea.getMinY());
    1088                    high = Math.min(high, dataArea.getMaxY());
    1089                    rect = new Rectangle2D.Double(dataArea.getMinX(),
    1090                            low, dataArea.getWidth(),
    1091                            high - low);
    1092                }
    1093                else if (orientation == PlotOrientation.VERTICAL) {
    1094                    // clip left and right bounds to data area
    1095                    low = Math.max(low, dataArea.getMinX());
    1096                    high = Math.min(high, dataArea.getMaxX());
    1097                    rect = new Rectangle2D.Double(low,
    1098                            dataArea.getMinY(), high - low,
    1099                            dataArea.getHeight());
    1100                }
    1101
    1102                final Composite originalComposite = g2.getComposite();
    1103                g2.setComposite(AlphaComposite.getInstance(
    1104                        AlphaComposite.SRC_OVER, marker.getAlpha()));
    1105                Paint p = marker.getPaint();
    1106                if (p instanceof GradientPaint) {
    1107                    GradientPaint gp = (GradientPaint) p;
    1108                    GradientPaintTransformer t = im.getGradientPaintTransformer();
    1109                    if (t != null) {
    1110                        gp = t.transform(gp, rect);
    1111                    }
    1112                    g2.setPaint(gp);
    1113                }
    1114                else {
    1115                    g2.setPaint(p);
    1116                }
    1117                g2.fill(rect);
    1118
    1119                // now draw the outlines, if visible...
    1120                if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
    1121                    if (orientation == PlotOrientation.VERTICAL) {
    1122                        Line2D line = new Line2D.Double();
    1123                        double y0 = dataArea.getMinY();
    1124                        double y1 = dataArea.getMaxY();
    1125                        g2.setPaint(im.getOutlinePaint());
    1126                        g2.setStroke(im.getOutlineStroke());
    1127                        if (range.contains(start)) {
    1128                            line.setLine(start2d, y0, start2d, y1);
    1129                            g2.draw(line);
    1130                        }
    1131                        if (range.contains(end)) {
    1132                            line.setLine(end2d, y0, end2d, y1);
    1133                            g2.draw(line);
    1134                        }
    1135                    }
    1136                    else { // PlotOrientation.HORIZONTAL
    1137                        Line2D line = new Line2D.Double();
    1138                        double x0 = dataArea.getMinX();
    1139                        double x1 = dataArea.getMaxX();
    1140                        g2.setPaint(im.getOutlinePaint());
    1141                        g2.setStroke(im.getOutlineStroke());
    1142                        if (range.contains(start)) {
    1143                            line.setLine(x0, start2d, x1, start2d);
    1144                            g2.draw(line);
    1145                        }
    1146                        if (range.contains(end)) {
    1147                            line.setLine(x0, end2d, x1, end2d);
    1148                            g2.draw(line);
    1149                        }
    1150                    }
    1151                }
    1152
    1153                String label = marker.getLabel();
    1154                RectangleAnchor anchor = marker.getLabelAnchor();
    1155                if (label != null) {
    1156                    Font labelFont = marker.getLabelFont();
    1157                    g2.setFont(labelFont);
    1158                    g2.setPaint(marker.getLabelPaint());
    1159                    Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
    1160                            g2, orientation, dataArea, rect,
    1161                            marker.getLabelOffset(), marker.getLabelOffsetType(),
    1162                            anchor);
    1163                    TextUtilities.drawAlignedString(label, g2,
    1164                            (float) coordinates.getX(), (float) coordinates.getY(),
    1165                            marker.getLabelTextAnchor());
    1166                }
    1167                g2.setComposite(originalComposite);
    1168
    1169            }
    1170
    1171        }
    1172
    1173        /**
    1174         * Calculates the (x, y) coordinates for drawing a marker label.
    1175         *
    1176         * @param g2  the graphics device.
    1177         * @param orientation  the plot orientation.
    1178         * @param dataArea  the data area.
    1179         * @param markerArea  the rectangle surrounding the marker area.
    1180         * @param markerOffset  the marker label offset.
    1181         * @param labelOffsetType  the label offset type.
    1182         * @param anchor  the label anchor.
    1183         *
    1184         * @return The coordinates for drawing the marker label.
    1185         */
    1186        protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
    1187                PlotOrientation orientation,
    1188                Rectangle2D dataArea,
    1189                Rectangle2D markerArea,
    1190                RectangleInsets markerOffset,
    1191                LengthAdjustmentType labelOffsetType,
    1192                RectangleAnchor anchor) {
    1193
    1194            Rectangle2D anchorRect = null;
    1195            if (orientation == PlotOrientation.HORIZONTAL) {
    1196                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
    1197                        LengthAdjustmentType.CONTRACT, labelOffsetType);
    1198            }
    1199            else if (orientation == PlotOrientation.VERTICAL) {
    1200                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
    1201                        labelOffsetType, LengthAdjustmentType.CONTRACT);
    1202            }
    1203            return RectangleAnchor.coordinates(anchorRect, anchor);
    1204
    1205        }
    1206
    1207        /**
    1208         * Draws a horizontal line across the chart to represent a 'range marker'.
    1209         *
    1210         * @param g2  the graphics device.
    1211         * @param plot  the plot.
    1212         * @param rangeAxis  the range axis.
    1213         * @param marker  the marker line.
    1214         * @param dataArea  the axis data area.
    1215         */
    1216        public void drawRangeMarker(Graphics2D g2,
    1217                                    XYPlot plot,
    1218                                    ValueAxis rangeAxis,
    1219                                    Marker marker,
    1220                                    Rectangle2D dataArea) {
    1221
    1222            if (marker instanceof ValueMarker) {
    1223                ValueMarker vm = (ValueMarker) marker;
    1224                double value = vm.getValue();
    1225                Range range = rangeAxis.getRange();
    1226                if (!range.contains(value)) {
    1227                    return;
    1228                }
    1229
    1230                double v = rangeAxis.valueToJava2D(value, dataArea,
    1231                        plot.getRangeAxisEdge());
    1232                PlotOrientation orientation = plot.getOrientation();
    1233                Line2D line = null;
    1234                if (orientation == PlotOrientation.HORIZONTAL) {
    1235                    line = new Line2D.Double(v, dataArea.getMinY(), v,
    1236                            dataArea.getMaxY());
    1237                }
    1238                else if (orientation == PlotOrientation.VERTICAL) {
    1239                    line = new Line2D.Double(dataArea.getMinX(), v,
    1240                            dataArea.getMaxX(), v);
    1241                }
    1242
    1243                final Composite originalComposite = g2.getComposite();
    1244                g2.setComposite(AlphaComposite.getInstance(
    1245                        AlphaComposite.SRC_OVER, marker.getAlpha()));
    1246                g2.setPaint(marker.getPaint());
    1247                g2.setStroke(marker.getStroke());
    1248                g2.draw(line);
    1249
    1250                String label = marker.getLabel();
    1251                RectangleAnchor anchor = marker.getLabelAnchor();
    1252                if (label != null) {
    1253                    Font labelFont = marker.getLabelFont();
    1254                    g2.setFont(labelFont);
    1255                    g2.setPaint(marker.getLabelPaint());
    1256                    Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
    1257                            g2, orientation, dataArea, line.getBounds2D(),
    1258                            marker.getLabelOffset(),
    1259                            LengthAdjustmentType.EXPAND, anchor);
    1260                    TextUtilities.drawAlignedString(label, g2,
    1261                            (float) coordinates.getX(), (float) coordinates.getY(),
    1262                            marker.getLabelTextAnchor());
    1263                }
    1264                g2.setComposite(originalComposite);
    1265            }
    1266            else if (marker instanceof IntervalMarker) {
    1267                IntervalMarker im = (IntervalMarker) marker;
    1268                double start = im.getStartValue();
    1269                double end = im.getEndValue();
    1270                Range range = rangeAxis.getRange();
    1271                if (!(range.intersects(start, end))) {
    1272                    return;
    1273                }
    1274
    1275                double start2d = rangeAxis.valueToJava2D(start, dataArea,
    1276                        plot.getRangeAxisEdge());
    1277                double end2d = rangeAxis.valueToJava2D(end, dataArea,
    1278                        plot.getRangeAxisEdge());
    1279                double low = Math.min(start2d, end2d);
    1280                double high = Math.max(start2d, end2d);
    1281
    1282                PlotOrientation orientation = plot.getOrientation();
    1283                Rectangle2D rect = null;
    1284                if (orientation == PlotOrientation.HORIZONTAL) {
    1285                    // clip left and right bounds to data area
    1286                    low = Math.max(low, dataArea.getMinX());
    1287                    high = Math.min(high, dataArea.getMaxX());
    1288                    rect = new Rectangle2D.Double(low,
    1289                            dataArea.getMinY(), high - low,
    1290                            dataArea.getHeight());
    1291                }
    1292                else if (orientation == PlotOrientation.VERTICAL) {
    1293                    // clip top and bottom bounds to data area
    1294                    low = Math.max(low, dataArea.getMinY());
    1295                    high = Math.min(high, dataArea.getMaxY());
    1296                    rect = new Rectangle2D.Double(dataArea.getMinX(),
    1297                            low, dataArea.getWidth(),
    1298                            high - low);
    1299                }
    1300
    1301                final Composite originalComposite = g2.getComposite();
    1302                g2.setComposite(AlphaComposite.getInstance(
    1303                        AlphaComposite.SRC_OVER, marker.getAlpha()));
    1304                Paint p = marker.getPaint();
    1305                if (p instanceof GradientPaint) {
    1306                    GradientPaint gp = (GradientPaint) p;
    1307                    GradientPaintTransformer t = im.getGradientPaintTransformer();
    1308                    if (t != null) {
    1309                        gp = t.transform(gp, rect);
    1310                    }
    1311                    g2.setPaint(gp);
    1312                }
    1313                else {
    1314                    g2.setPaint(p);
    1315                }
    1316                g2.fill(rect);
    1317
    1318                // now draw the outlines, if visible...
    1319                if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
    1320                    if (orientation == PlotOrientation.VERTICAL) {
    1321                        Line2D line = new Line2D.Double();
    1322                        double x0 = dataArea.getMinX();
    1323                        double x1 = dataArea.getMaxX();
    1324                        g2.setPaint(im.getOutlinePaint());
    1325                        g2.setStroke(im.getOutlineStroke());
    1326                        if (range.contains(start)) {
    1327                            line.setLine(x0, start2d, x1, start2d);
    1328                            g2.draw(line);
    1329                        }
    1330                        if (range.contains(end)) {
    1331                            line.setLine(x0, end2d, x1, end2d);
    1332                            g2.draw(line);
    1333                        }
    1334                    }
    1335                    else { // PlotOrientation.HORIZONTAL
    1336                        Line2D line = new Line2D.Double();
    1337                        double y0 = dataArea.getMinY();
    1338                        double y1 = dataArea.getMaxY();
    1339                        g2.setPaint(im.getOutlinePaint());
    1340                        g2.setStroke(im.getOutlineStroke());
    1341                        if (range.contains(start)) {
    1342                            line.setLine(start2d, y0, start2d, y1);
    1343                            g2.draw(line);
    1344                        }
    1345                        if (range.contains(end)) {
    1346                            line.setLine(end2d, y0, end2d, y1);
    1347                            g2.draw(line);
    1348                        }
    1349                    }
    1350                }
    1351
    1352                String label = marker.getLabel();
    1353                RectangleAnchor anchor = marker.getLabelAnchor();
    1354                if (label != null) {
    1355                    Font labelFont = marker.getLabelFont();
    1356                    g2.setFont(labelFont);
    1357                    g2.setPaint(marker.getLabelPaint());
    1358                    Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
    1359                            g2, orientation, dataArea, rect,
    1360                            marker.getLabelOffset(), marker.getLabelOffsetType(),
    1361                            anchor);
    1362                    TextUtilities.drawAlignedString(label, g2,
    1363                            (float) coordinates.getX(), (float) coordinates.getY(),
    1364                            marker.getLabelTextAnchor());
    1365                }
    1366                g2.setComposite(originalComposite);
    1367            }
    1368        }
    1369
    1370        /**
    1371         * Calculates the (x, y) coordinates for drawing a marker label.
    1372         *
    1373         * @param g2  the graphics device.
    1374         * @param orientation  the plot orientation.
    1375         * @param dataArea  the data area.
    1376         * @param markerArea  the marker area.
    1377         * @param markerOffset  the marker offset.
    1378         * @param labelOffsetForRange  ??
    1379         * @param anchor  the label anchor.
    1380         *
    1381         * @return The coordinates for drawing the marker label.
    1382         */
    1383        private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2,
    1384                                          PlotOrientation orientation,
    1385                                          Rectangle2D dataArea,
    1386                                          Rectangle2D markerArea,
    1387                                          RectangleInsets markerOffset,
    1388                                          LengthAdjustmentType labelOffsetForRange,
    1389                                          RectangleAnchor anchor) {
    1390
    1391            Rectangle2D anchorRect = null;
    1392            if (orientation == PlotOrientation.HORIZONTAL) {
    1393                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
    1394                        labelOffsetForRange, LengthAdjustmentType.CONTRACT);
    1395            }
    1396            else if (orientation == PlotOrientation.VERTICAL) {
    1397                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
    1398                        LengthAdjustmentType.CONTRACT, labelOffsetForRange);
    1399            }
    1400            return RectangleAnchor.coordinates(anchorRect, anchor);
    1401
    1402        }
    1403
    1404        /**
    1405         * Returns a clone of the renderer.
    1406         *
    1407         * @return A clone.
    1408         *
    1409         * @throws CloneNotSupportedException if the renderer does not support
    1410         *         cloning.
    1411         */
    1412        protected Object clone() throws CloneNotSupportedException {
    1413            AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone();
    1414            // 'plot' : just retain reference, not a deep copy
    1415
    1416            if (this.itemLabelGenerator != null
    1417                    && this.itemLabelGenerator instanceof PublicCloneable) {
    1418                PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator;
    1419                clone.itemLabelGenerator = (XYItemLabelGenerator) pc.clone();
    1420            }
    1421            clone.itemLabelGeneratorList
    1422                    = (ObjectList) this.itemLabelGeneratorList.clone();
    1423            if (this.baseItemLabelGenerator != null
    1424                    && this.baseItemLabelGenerator instanceof PublicCloneable) {
    1425                PublicCloneable pc = (PublicCloneable) this.baseItemLabelGenerator;
    1426                clone.baseItemLabelGenerator = (XYItemLabelGenerator) pc.clone();
    1427            }
    1428
    1429            if (this.toolTipGenerator != null
    1430                    && this.toolTipGenerator instanceof PublicCloneable) {
    1431                PublicCloneable pc = (PublicCloneable) this.toolTipGenerator;
    1432                clone.toolTipGenerator = (XYToolTipGenerator) pc.clone();
    1433            }
    1434            clone.toolTipGeneratorList
    1435                    = (ObjectList) this.toolTipGeneratorList.clone();
    1436            if (this.baseToolTipGenerator != null
    1437                    && this.baseToolTipGenerator instanceof PublicCloneable) {
    1438                PublicCloneable pc = (PublicCloneable) this.baseToolTipGenerator;
    1439                clone.baseToolTipGenerator = (XYToolTipGenerator) pc.clone();
    1440            }
    1441
    1442            if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
    1443                clone.legendItemLabelGenerator = (XYSeriesLabelGenerator)
    1444                        ObjectUtilities.clone(this.legendItemLabelGenerator);
    1445            }
    1446            if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
    1447                clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator)
    1448                        ObjectUtilities.clone(this.legendItemToolTipGenerator);
    1449            }
    1450            if (clone.legendItemURLGenerator instanceof PublicCloneable) {
    1451                clone.legendItemURLGenerator = (XYSeriesLabelGenerator)
    1452                        ObjectUtilities.clone(this.legendItemURLGenerator);
    1453            }
    1454
    1455            clone.foregroundAnnotations = (List) ObjectUtilities.deepClone(
    1456                    this.foregroundAnnotations);
    1457            clone.backgroundAnnotations = (List) ObjectUtilities.deepClone(
    1458                    this.backgroundAnnotations);
    1459
    1460            if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
    1461                clone.legendItemLabelGenerator = (XYSeriesLabelGenerator)
    1462                        ObjectUtilities.clone(this.legendItemLabelGenerator);
    1463            }
    1464            if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
    1465                clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator)
    1466                        ObjectUtilities.clone(this.legendItemToolTipGenerator);
    1467            }
    1468            if (clone.legendItemURLGenerator instanceof PublicCloneable) {
    1469                clone.legendItemURLGenerator = (XYSeriesLabelGenerator)
    1470                        ObjectUtilities.clone(this.legendItemURLGenerator);
    1471            }
    1472
    1473            return clone;
    1474        }
    1475
    1476        /**
    1477         * Tests this renderer for equality with another object.
    1478         *
    1479         * @param obj  the object (<code>null</code> permitted).
    1480         *
    1481         * @return <code>true</code> or <code>false</code>.
    1482         */
    1483        public boolean equals(Object obj) {
    1484            if (obj == this) {
    1485                return true;
    1486            }
    1487            if (!(obj instanceof AbstractXYItemRenderer)) {
    1488                return false;
    1489            }
    1490            AbstractXYItemRenderer that = (AbstractXYItemRenderer) obj;
    1491            if (!ObjectUtilities.equal(this.itemLabelGenerator,
    1492                    that.itemLabelGenerator)) {
    1493                return false;
    1494            }
    1495            if (!this.itemLabelGeneratorList.equals(that.itemLabelGeneratorList)) {
    1496                return false;
    1497            }
    1498            if (!ObjectUtilities.equal(this.baseItemLabelGenerator,
    1499                    that.baseItemLabelGenerator)) {
    1500                return false;
    1501            }
    1502            if (!ObjectUtilities.equal(this.toolTipGenerator,
    1503                    that.toolTipGenerator)) {
    1504                return false;
    1505            }
    1506            if (!this.toolTipGeneratorList.equals(that.toolTipGeneratorList)) {
    1507                return false;
    1508            }
    1509            if (!ObjectUtilities.equal(this.baseToolTipGenerator,
    1510                    that.baseToolTipGenerator)) {
    1511                return false;
    1512            }
    1513            if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
    1514                return false;
    1515            }
    1516            if (!this.foregroundAnnotations.equals(that.foregroundAnnotations)) {
    1517                return false;
    1518            }
    1519            if (!this.backgroundAnnotations.equals(that.backgroundAnnotations)) {
    1520                return false;
    1521            }
    1522            if (this.defaultEntityRadius != that.defaultEntityRadius) {
    1523                return false;
    1524            }
    1525            if (!ObjectUtilities.equal(this.legendItemLabelGenerator,
    1526                    that.legendItemLabelGenerator)) {
    1527                return false;
    1528            }
    1529            if (!ObjectUtilities.equal(this.legendItemToolTipGenerator,
    1530                    that.legendItemToolTipGenerator)) {
    1531                return false;
    1532            }
    1533            if (!ObjectUtilities.equal(this.legendItemURLGenerator,
    1534                    that.legendItemURLGenerator)) {
    1535                return false;
    1536            }
    1537            return super.equals(obj);
    1538        }
    1539
    1540        /**
    1541         * Returns the drawing supplier from the plot.
    1542         *
    1543         * @return The drawing supplier (possibly <code>null</code>).
    1544         */
    1545        public DrawingSupplier getDrawingSupplier() {
    1546            DrawingSupplier result = null;
    1547            XYPlot p = getPlot();
    1548            if (p != null) {
    1549                result = p.getDrawingSupplier();
    1550            }
    1551            return result;
    1552        }
    1553
    1554        /**
    1555         * Considers the current (x, y) coordinate and updates the crosshair point
    1556         * if it meets the criteria (usually means the (x, y) coordinate is the
    1557         * closest to the anchor point so far).
    1558         *
    1559         * @param crosshairState  the crosshair state (<code>null</code> permitted,
    1560         *                        but the method does nothing in that case).
    1561         * @param x  the x-value (in data space).
    1562         * @param y  the y-value (in data space).
    1563         * @param transX  the x-value translated to Java2D space.
    1564         * @param transY  the y-value translated to Java2D space.
    1565         * @param orientation  the plot orientation (<code>null</code> not
    1566         *                     permitted).
    1567         *
    1568         * @deprecated Use {@link #updateCrosshairValues(CrosshairState, double,
    1569         *         double, int, int, double, double, PlotOrientation)} -- see bug
    1570         *         report 1086307.
    1571         */
    1572        protected void updateCrosshairValues(CrosshairState crosshairState,
    1573                double x, double y, double transX, double transY,
    1574                PlotOrientation orientation) {
    1575            updateCrosshairValues(crosshairState, x, y, 0, 0, transX, transY,
    1576                    orientation);
    1577        }
    1578
    1579        /**
    1580         * Considers the current (x, y) coordinate and updates the crosshair point
    1581         * if it meets the criteria (usually means the (x, y) coordinate is the
    1582         * closest to the anchor point so far).
    1583         *
    1584         * @param crosshairState  the crosshair state (<code>null</code> permitted,
    1585         *                        but the method does nothing in that case).
    1586         * @param x  the x-value (in data space).
    1587         * @param y  the y-value (in data space).
    1588         * @param domainAxisIndex  the index of the domain axis for the point.
    1589         * @param rangeAxisIndex  the index of the range axis for the point.
    1590         * @param transX  the x-value translated to Java2D space.
    1591         * @param transY  the y-value translated to Java2D space.
    1592         * @param orientation  the plot orientation (<code>null</code> not
    1593         *                     permitted).
    1594         *
    1595         * @since 1.0.4
    1596         */
    1597        protected void updateCrosshairValues(CrosshairState crosshairState,
    1598                double x, double y, int domainAxisIndex, int rangeAxisIndex,
    1599                double transX, double transY, PlotOrientation orientation) {
    1600
    1601            if (orientation == null) {
    1602                throw new IllegalArgumentException("Null 'orientation' argument.");
    1603            }
    1604
    1605            if (crosshairState != null) {
    1606                // do we need to update the crosshair values?
    1607                if (this.plot.isDomainCrosshairLockedOnData()) {
    1608                    if (this.plot.isRangeCrosshairLockedOnData()) {
    1609                        // both axes
    1610                        crosshairState.updateCrosshairPoint(x, y, domainAxisIndex,
    1611                                rangeAxisIndex, transX, transY, orientation);
    1612                    }
    1613                    else {
    1614                        // just the domain axis...
    1615                        crosshairState.updateCrosshairX(x, domainAxisIndex);
    1616                    }
    1617                }
    1618                else {
    1619                    if (this.plot.isRangeCrosshairLockedOnData()) {
    1620                        // just the range axis...
    1621                        crosshairState.updateCrosshairY(y, rangeAxisIndex);
    1622                    }
    1623                }
    1624            }
    1625
    1626        }
    1627
    1628        /**
    1629         * Draws an item label.
    1630         *
    1631         * @param g2  the graphics device.
    1632         * @param orientation  the orientation.
    1633         * @param dataset  the dataset.
    1634         * @param series  the series index (zero-based).
    1635         * @param item  the item index (zero-based).
    1636         * @param x  the x coordinate (in Java2D space).
    1637         * @param y  the y coordinate (in Java2D space).
    1638         * @param negative  indicates a negative value (which affects the item
    1639         *                  label position).
    1640         */
    1641        protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation,
    1642                XYDataset dataset, int series, int item, double x, double y,
    1643                boolean negative) {
    1644
    1645            XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
    1646            if (generator != null) {
    1647                Font labelFont = getItemLabelFont(series, item);
    1648                Paint paint = getItemLabelPaint(series, item);
    1649                g2.setFont(labelFont);
    1650                g2.setPaint(paint);
    1651                String label = generator.generateLabel(dataset, series, item);
    1652
    1653                // get the label position..
    1654                ItemLabelPosition position = null;
    1655                if (!negative) {
    1656                    position = getPositiveItemLabelPosition(series, item);
    1657                }
    1658                else {
    1659                    position = getNegativeItemLabelPosition(series, item);
    1660                }
    1661
    1662                // work out the label anchor point...
    1663                Point2D anchorPoint = calculateLabelAnchorPoint(
    1664                        position.getItemLabelAnchor(), x, y, orientation);
    1665                TextUtilities.drawRotatedString(label, g2,
    1666                        (float) anchorPoint.getX(), (float) anchorPoint.getY(),
    1667                        position.getTextAnchor(), position.getAngle(),
    1668                        position.getRotationAnchor());
    1669            }
    1670
    1671        }
    1672
    1673        /**
    1674         * Draws all the annotations for the specified layer.
    1675         *
    1676         * @param g2  the graphics device.
    1677         * @param dataArea  the data area.
    1678         * @param domainAxis  the domain axis.
    1679         * @param rangeAxis  the range axis.
    1680         * @param layer  the layer.
    1681         * @param info  the plot rendering info.
    1682         */
    1683        public void drawAnnotations(Graphics2D g2,
    1684                                    Rectangle2D dataArea,
    1685                                    ValueAxis domainAxis,
    1686                                    ValueAxis rangeAxis,
    1687                                    Layer layer,
    1688                                    PlotRenderingInfo info) {
    1689
    1690            Iterator iterator = null;
    1691            if (layer.equals(Layer.FOREGROUND)) {
    1692                iterator = this.foregroundAnnotations.iterator();
    1693            }
    1694            else if (layer.equals(Layer.BACKGROUND)) {
    1695                iterator = this.backgroundAnnotations.iterator();
    1696            }
    1697            else {
    1698                // should not get here
    1699                throw new RuntimeException("Unknown layer.");
    1700            }
    1701            while (iterator.hasNext()) {
    1702                XYAnnotation annotation = (XYAnnotation) iterator.next();
    1703                annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis,
    1704                        0, info);
    1705            }
    1706
    1707        }
    1708
    1709        /**
    1710         * Adds an entity to the collection.
    1711         *
    1712         * @param entities  the entity collection being populated.
    1713         * @param area  the entity area (if <code>null</code> a default will be
    1714         *              used).
    1715         * @param dataset  the dataset.
    1716         * @param series  the series.
    1717         * @param item  the item.
    1718         * @param entityX  the entity's center x-coordinate in user space (only
    1719         *                 used if <code>area</code> is <code>null</code>).
    1720         * @param entityY  the entity's center y-coordinate in user space (only
    1721         *                 used if <code>area</code> is <code>null</code>).
    1722         */
    1723        protected void addEntity(EntityCollection entities, Shape area,
    1724                                 XYDataset dataset, int series, int item,
    1725                                 double entityX, double entityY) {
    1726            if (!getItemCreateEntity(series, item)) {
    1727                return;
    1728            }
    1729            Shape hotspot = area;
    1730            if (hotspot == null) {
    1731                    double w = this.defaultEntityRadius * 2;
    1732                    if (getPlot().getOrientation() == PlotOrientation.VERTICAL) {
    1733                            hotspot = new Ellipse2D.Double(
    1734                                            entityX - this.defaultEntityRadius,
    1735                                            entityY - this.defaultEntityRadius, w, w);
    1736                    }
    1737                    else {
    1738                            hotspot = new Ellipse2D.Double(
    1739                                            entityY - this.defaultEntityRadius,
    1740                                entityX - this.defaultEntityRadius, w, w);
    1741                    }
    1742            }
    1743            String tip = null;
    1744            XYToolTipGenerator generator = getToolTipGenerator(series, item);
    1745            if (generator != null) {
    1746                tip = generator.generateToolTip(dataset, series, item);
    1747            }
    1748            String url = null;
    1749            if (getURLGenerator() != null) {
    1750                url = getURLGenerator().generateURL(dataset, series, item);
    1751            }
    1752            XYItemEntity entity = new XYItemEntity(hotspot, dataset, series, item,
    1753                    tip, url);
    1754            entities.add(entity);
    1755        }
    1756
    1757        /**
    1758         * Returns <code>true</code> if the specified point (x, y) falls within or
    1759         * on the boundary of the specified rectangle.
    1760         *
    1761         * @param rect  the rectangle (<code>null</code> not permitted).
    1762         * @param x  the x-coordinate.
    1763         * @param y  the y-coordinate.
    1764         *
    1765         * @return A boolean.
    1766         *
    1767         * @since 1.0.10
    1768         */
    1769        public static boolean isPointInRect(Rectangle2D rect, double x, double y) {
    1770            // TODO: For JFreeChart 1.2.0, this method should go in the
    1771            //       ShapeUtilities class
    1772            return (x >= rect.getMinX() && x <= rect.getMaxX()
    1773                            && y >= rect.getMinY() && y <= rect.getMaxY());
    1774        }
    1775
    1776    }