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