001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, 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     * AbstractCategoryItemRenderer.java
029     * ---------------------------------
030     * (C) Copyright 2002-2007, by Object Refinery Limited.
031     *
032     * Original Author:  David Gilbert (for Object Refinery Limited);
033     * Contributor(s):   Richard Atkinson;
034     *
035     * Changes:
036     * --------
037     * 29-May-2002 : Version 1 (DG);
038     * 06-Jun-2002 : Added accessor methods for the tool tip generator (DG);
039     * 11-Jun-2002 : Made constructors protected (DG);
040     * 26-Jun-2002 : Added axis to initialise method (DG);
041     * 05-Aug-2002 : Added urlGenerator member variable plus accessors (RA);
042     * 22-Aug-2002 : Added categoriesPaint attribute, based on code submitted by
043     *               Janet Banks.  This can be used when there is only one series,
044     *               and you want each category item to have a different color (DG);
045     * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
046     * 29-Oct-2002 : Fixed bug where background image for plot was not being
047     *               drawn (DG);
048     * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
049     * 26-Nov 2002 : Replaced the isStacked() method with getRangeType() (DG);
050     * 09-Jan-2003 : Renamed grid-line methods (DG);
051     * 17-Jan-2003 : Moved plot classes into separate package (DG);
052     * 25-Mar-2003 : Implemented Serializable (DG);
053     * 12-May-2003 : Modified to take into account the plot orientation (DG);
054     * 12-Aug-2003 : Very minor javadoc corrections (DB)
055     * 13-Aug-2003 : Implemented Cloneable (DG);
056     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
057     * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
058     * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
059     * 11-Feb-2004 : Modified labelling for markers (DG);
060     * 12-Feb-2004 : Updated clone() method (DG);
061     * 15-Apr-2004 : Created a new CategoryToolTipGenerator interface (DG);
062     * 05-May-2004 : Fixed bug (948310) where interval markers extend outside axis
063     *               range (DG);
064     * 14-Jun-2004 : Fixed bug in drawRangeMarker() method - now uses 'paint' and
065     *               'stroke' rather than 'outlinePaint' and 'outlineStroke' (DG);
066     * 15-Jun-2004 : Interval markers can now use GradientPaint (DG);
067     * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
068     *               --> TextUtilities (DG);
069     * 01-Oct-2004 : Fixed bug 1029697, problem with label alignment in
070     *               drawRangeMarker() method (DG);
071     * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
072     * 21-Jan-2005 : Modified return type of calculateRangeMarkerTextAnchorPoint()
073     *               method (DG);
074     * 08-Mar-2005 : Fixed positioning of marker labels (DG);
075     * 20-Apr-2005 : Added legend label, tooltip and URL generators (DG);
076     * 01-Jun-2005 : Handle one dimension of the marker label adjustment
077     *               automatically (DG);
078     * 09-Jun-2005 : Added utility method for adding an item entity (DG);
079     * ------------- JFREECHART 1.0.x ---------------------------------------------
080     * 01-Mar-2006 : Updated getLegendItems() to check seriesVisibleInLegend
081     *               flags (DG);
082     * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
083     * 23-Oct-2006 : Draw outlines for interval markers (DG);
084     * 24-Oct-2006 : Respect alpha setting in markers, as suggested by Sergei
085     *               Ivanov in patch 1567843 (DG);
086     * 30-Nov-2006 : Added a check for series visibility in the getLegendItem()
087     *               method (DG);
088     * 07-Dec-2006 : Fix for equals() method (DG);
089     * 22-Feb-2007 : Added createState() method (DG);
090     * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to 
091     *               Sergei Ivanov) (DG);
092     * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
093     *               itemLabelGenerator, toolTipGenerator and itemURLGenerator
094     *               override fields (DG);
095     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
096     *
097     */
098    
099    package org.jfree.chart.renderer.category;
100    
101    import java.awt.AlphaComposite;
102    import java.awt.Composite;
103    import java.awt.Font;
104    import java.awt.GradientPaint;
105    import java.awt.Graphics2D;
106    import java.awt.Paint;
107    import java.awt.Shape;
108    import java.awt.Stroke;
109    import java.awt.geom.Line2D;
110    import java.awt.geom.Point2D;
111    import java.awt.geom.Rectangle2D;
112    import java.io.Serializable;
113    
114    import org.jfree.chart.LegendItem;
115    import org.jfree.chart.LegendItemCollection;
116    import org.jfree.chart.axis.CategoryAxis;
117    import org.jfree.chart.axis.ValueAxis;
118    import org.jfree.chart.entity.CategoryItemEntity;
119    import org.jfree.chart.entity.EntityCollection;
120    import org.jfree.chart.event.RendererChangeEvent;
121    import org.jfree.chart.labels.CategoryItemLabelGenerator;
122    import org.jfree.chart.labels.CategorySeriesLabelGenerator;
123    import org.jfree.chart.labels.CategoryToolTipGenerator;
124    import org.jfree.chart.labels.ItemLabelPosition;
125    import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator;
126    import org.jfree.chart.plot.CategoryMarker;
127    import org.jfree.chart.plot.CategoryPlot;
128    import org.jfree.chart.plot.DrawingSupplier;
129    import org.jfree.chart.plot.IntervalMarker;
130    import org.jfree.chart.plot.Marker;
131    import org.jfree.chart.plot.PlotOrientation;
132    import org.jfree.chart.plot.PlotRenderingInfo;
133    import org.jfree.chart.plot.ValueMarker;
134    import org.jfree.chart.renderer.AbstractRenderer;
135    import org.jfree.chart.urls.CategoryURLGenerator;
136    import org.jfree.data.Range;
137    import org.jfree.data.category.CategoryDataset;
138    import org.jfree.data.general.DatasetUtilities;
139    import org.jfree.text.TextUtilities;
140    import org.jfree.ui.GradientPaintTransformer;
141    import org.jfree.ui.LengthAdjustmentType;
142    import org.jfree.ui.RectangleAnchor;
143    import org.jfree.ui.RectangleInsets;
144    import org.jfree.util.ObjectList;
145    import org.jfree.util.ObjectUtilities;
146    import org.jfree.util.PublicCloneable;
147    
148    /**
149     * An abstract base class that you can use to implement a new
150     * {@link CategoryItemRenderer}.  When you create a new
151     * {@link CategoryItemRenderer} you are not required to extend this class,
152     * but it makes the job easier.
153     */
154    public abstract class AbstractCategoryItemRenderer extends AbstractRenderer
155        implements CategoryItemRenderer, Cloneable, PublicCloneable, Serializable {
156    
157        /** For serialization. */
158        private static final long serialVersionUID = 1247553218442497391L;
159    
160        /** The plot that the renderer is assigned to. */
161        private CategoryPlot plot;
162    
163        /** 
164         * The item label generator for ALL series. 
165         * 
166         * @deprecated This field is redundant and deprecated as of version 1.0.6.
167         */
168        private CategoryItemLabelGenerator itemLabelGenerator;
169    
170        /** A list of item label generators (one per series). */
171        private ObjectList itemLabelGeneratorList;
172    
173        /** The base item label generator. */
174        private CategoryItemLabelGenerator baseItemLabelGenerator;
175    
176        /** 
177         * The tool tip generator for ALL series. 
178         * 
179         * @deprecated This field is redundant and deprecated as of version 1.0.6.
180         */
181        private CategoryToolTipGenerator toolTipGenerator;
182    
183        /** A list of tool tip generators (one per series). */
184        private ObjectList toolTipGeneratorList;
185    
186        /** The base tool tip generator. */
187        private CategoryToolTipGenerator baseToolTipGenerator;
188    
189        /** 
190         * The URL generator. 
191         * 
192         * @deprecated This field is redundant and deprecated as of version 1.0.6.
193         */
194        private CategoryURLGenerator itemURLGenerator;
195    
196        /** A list of item label generators (one per series). */
197        private ObjectList itemURLGeneratorList;
198    
199        /** The base item label generator. */
200        private CategoryURLGenerator baseItemURLGenerator;
201    
202        /** The legend item label generator. */
203        private CategorySeriesLabelGenerator legendItemLabelGenerator;
204    
205        /** The legend item tool tip generator. */
206        private CategorySeriesLabelGenerator legendItemToolTipGenerator;
207    
208        /** The legend item URL generator. */
209        private CategorySeriesLabelGenerator legendItemURLGenerator;
210    
211        /** The number of rows in the dataset (temporary record). */
212        private transient int rowCount;
213    
214        /** The number of columns in the dataset (temporary record). */
215        private transient int columnCount;
216    
217        /**
218         * Creates a new renderer with no tool tip generator and no URL generator.
219         * The defaults (no tool tip or URL generators) have been chosen to
220         * minimise the processing required to generate a default chart.  If you
221         * require tool tips or URLs, then you can easily add the required
222         * generators.
223         */
224        protected AbstractCategoryItemRenderer() {
225            this.itemLabelGenerator = null;
226            this.itemLabelGeneratorList = new ObjectList();
227            this.toolTipGenerator = null;
228            this.toolTipGeneratorList = new ObjectList();
229            this.itemURLGenerator = null;
230            this.itemURLGeneratorList = new ObjectList();
231            this.legendItemLabelGenerator
232                = new StandardCategorySeriesLabelGenerator();
233        }
234    
235        /**
236         * Returns the number of passes through the dataset required by the
237         * renderer.  This method returns <code>1</code>, subclasses should
238         * override if they need more passes.
239         *
240         * @return The pass count.
241         */
242        public int getPassCount() {
243            return 1;
244        }
245    
246        /**
247         * Returns the plot that the renderer has been assigned to (where
248         * <code>null</code> indicates that the renderer is not currently assigned
249         * to a plot).
250         *
251         * @return The plot (possibly <code>null</code>).
252         *
253         * @see #setPlot(CategoryPlot)
254         */
255        public CategoryPlot getPlot() {
256            return this.plot;
257        }
258    
259        /**
260         * Sets the plot that the renderer has been assigned to.  This method is
261         * usually called by the {@link CategoryPlot}, in normal usage you
262         * shouldn't need to call this method directly.
263         *
264         * @param plot  the plot (<code>null</code> not permitted).
265         *
266         * @see #getPlot()
267         */
268        public void setPlot(CategoryPlot plot) {
269            if (plot == null) {
270                throw new IllegalArgumentException("Null 'plot' argument.");
271            }
272            this.plot = plot;
273        }
274    
275        // ITEM LABEL GENERATOR
276    
277        /**
278         * Returns the item label generator for a data item.  This implementation
279         * simply passes control to the {@link #getSeriesItemLabelGenerator(int)}
280         * method.  If, for some reason, you want a different generator for
281         * individual items, you can override this method.
282         *
283         * @param row  the row index (zero based).
284         * @param column  the column index (zero based).
285         *
286         * @return The generator (possibly <code>null</code>).
287         */
288        public CategoryItemLabelGenerator getItemLabelGenerator(int row,
289                int column) {
290            return getSeriesItemLabelGenerator(row);
291        }
292    
293        /**
294         * Returns the item label generator for a series.
295         *
296         * @param series  the series index (zero based).
297         *
298         * @return The generator (possibly <code>null</code>).
299         *
300         * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator)
301         */
302        public CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series) {
303    
304            // return the generator for ALL series, if there is one...
305            if (this.itemLabelGenerator != null) {
306                return this.itemLabelGenerator;
307            }
308    
309            // otherwise look up the generator table
310            CategoryItemLabelGenerator generator = (CategoryItemLabelGenerator)
311                this.itemLabelGeneratorList.get(series);
312            if (generator == null) {
313                generator = this.baseItemLabelGenerator;
314            }
315            return generator;
316    
317        }
318    
319        /**
320         * Sets the item label generator for ALL series and sends a
321         * {@link RendererChangeEvent} to all registered listeners.
322         *
323         * @param generator  the generator (<code>null</code> permitted).
324         * 
325         * @deprecated This method should no longer be used (as of version 1.0.6). 
326         *     It is sufficient to rely on {@link #setSeriesItemLabelGenerator(int, 
327         *     CategoryItemLabelGenerator)} and 
328         *     {@link #setBaseItemLabelGenerator(CategoryItemLabelGenerator)}.
329         */
330        public void setItemLabelGenerator(CategoryItemLabelGenerator generator) {
331            this.itemLabelGenerator = generator;
332            fireChangeEvent();
333        }
334    
335        /**
336         * Sets the item label generator for a series and sends a
337         * {@link RendererChangeEvent} to all registered listeners.
338         *
339         * @param series  the series index (zero based).
340         * @param generator  the generator (<code>null</code> permitted).
341         *
342         * @see #getSeriesItemLabelGenerator(int)
343         */
344        public void setSeriesItemLabelGenerator(int series,
345                                            CategoryItemLabelGenerator generator) {
346            this.itemLabelGeneratorList.set(series, generator);
347            fireChangeEvent();
348        }
349    
350        /**
351         * Returns the base item label generator.
352         *
353         * @return The generator (possibly <code>null</code>).
354         *
355         * @see #setBaseItemLabelGenerator(CategoryItemLabelGenerator)
356         */
357        public CategoryItemLabelGenerator getBaseItemLabelGenerator() {
358            return this.baseItemLabelGenerator;
359        }
360    
361        /**
362         * Sets the base item label generator and sends a
363         * {@link RendererChangeEvent} to all registered listeners.
364         *
365         * @param generator  the generator (<code>null</code> permitted).
366         *
367         * @see #getBaseItemLabelGenerator()
368         */
369        public void setBaseItemLabelGenerator(
370                CategoryItemLabelGenerator generator) {
371            this.baseItemLabelGenerator = generator;
372            fireChangeEvent();
373        }
374    
375        // TOOL TIP GENERATOR
376    
377        /**
378         * Returns the tool tip generator that should be used for the specified
379         * item.  This method looks up the generator using the "three-layer"
380         * approach outlined in the general description of this interface.  You
381         * can override this method if you want to return a different generator per
382         * item.
383         *
384         * @param row  the row index (zero-based).
385         * @param column  the column index (zero-based).
386         *
387         * @return The generator (possibly <code>null</code>).
388         */
389        public CategoryToolTipGenerator getToolTipGenerator(int row, int column) {
390    
391            CategoryToolTipGenerator result = null;
392            if (this.toolTipGenerator != null) {
393                result = this.toolTipGenerator;
394            }
395            else {
396                result = getSeriesToolTipGenerator(row);
397                if (result == null) {
398                    result = this.baseToolTipGenerator;
399                }
400            }
401            return result;
402        }
403    
404        /**
405         * Returns the tool tip generator that will be used for ALL items in the
406         * dataset (the "layer 0" generator).
407         *
408         * @return A tool tip generator (possibly <code>null</code>).
409         *
410         * @see #setToolTipGenerator(CategoryToolTipGenerator)
411         * 
412         * @deprecated This method should no longer be used (as of version 1.0.6). 
413         *     It is sufficient to rely on {@link #getSeriesToolTipGenerator(int)} 
414         *     and {@link #getBaseToolTipGenerator()}.
415         */
416        public CategoryToolTipGenerator getToolTipGenerator() {
417            return this.toolTipGenerator;
418        }
419    
420        /**
421         * Sets the tool tip generator for ALL series and sends a
422         * {@link org.jfree.chart.event.RendererChangeEvent} to all registered
423         * listeners.
424         *
425         * @param generator  the generator (<code>null</code> permitted).
426         *
427         * @see #getToolTipGenerator()
428         * 
429         * @deprecated This method should no longer be used (as of version 1.0.6). 
430         *     It is sufficient to rely on {@link #setSeriesToolTipGenerator(int, 
431         *     CategoryToolTipGenerator)} and 
432         *     {@link #setBaseToolTipGenerator(CategoryToolTipGenerator)}.
433         */
434        public void setToolTipGenerator(CategoryToolTipGenerator generator) {
435            this.toolTipGenerator = generator;
436            fireChangeEvent();
437        }
438    
439        /**
440         * Returns the tool tip generator for the specified series (a "layer 1"
441         * generator).
442         *
443         * @param series  the series index (zero-based).
444         *
445         * @return The tool tip generator (possibly <code>null</code>).
446         *
447         * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator)
448         */
449        public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) {
450            return (CategoryToolTipGenerator) this.toolTipGeneratorList.get(series);
451        }
452    
453        /**
454         * Sets the tool tip generator for a series and sends a 
455         * {@link RendererChangeEvent} to all registered listeners.
456         *
457         * @param series  the series index (zero-based).
458         * @param generator  the generator (<code>null</code> permitted).
459         *
460         * @see #getSeriesToolTipGenerator(int)
461         */
462        public void setSeriesToolTipGenerator(int series,
463                                              CategoryToolTipGenerator generator) {
464            this.toolTipGeneratorList.set(series, generator);
465            fireChangeEvent();
466        }
467    
468        /**
469         * Returns the base tool tip generator (the "layer 2" generator).
470         *
471         * @return The tool tip generator (possibly <code>null</code>).
472         *
473         * @see #setBaseToolTipGenerator(CategoryToolTipGenerator)
474         */
475        public CategoryToolTipGenerator getBaseToolTipGenerator() {
476            return this.baseToolTipGenerator;
477        }
478    
479        /**
480         * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
481         * to all registered listeners.
482         *
483         * @param generator  the generator (<code>null</code> permitted).
484         *
485         * @see #getBaseToolTipGenerator()
486         */
487        public void setBaseToolTipGenerator(CategoryToolTipGenerator generator) {
488            this.baseToolTipGenerator = generator;
489            fireChangeEvent();
490        }
491    
492        // URL GENERATOR
493    
494        /**
495         * Returns the URL generator for a data item.  This method just calls the
496         * getSeriesItemURLGenerator method, but you can override this behaviour if
497         * you want to.
498         *
499         * @param row  the row index (zero based).
500         * @param column  the column index (zero based).
501         *
502         * @return The URL generator.
503         */
504        public CategoryURLGenerator getItemURLGenerator(int row, int column) {
505            return getSeriesItemURLGenerator(row);
506        }
507    
508        /**
509         * Returns the URL generator for a series.
510         *
511         * @param series  the series index (zero based).
512         *
513         * @return The URL generator for the series.
514         *
515         * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator)
516         */
517        public CategoryURLGenerator getSeriesItemURLGenerator(int series) {
518    
519            // return the generator for ALL series, if there is one...
520            if (this.itemURLGenerator != null) {
521                return this.itemURLGenerator;
522            }
523    
524            // otherwise look up the generator table
525            CategoryURLGenerator generator
526                = (CategoryURLGenerator) this.itemURLGeneratorList.get(series);
527            if (generator == null) {
528                generator = this.baseItemURLGenerator;
529            }
530            return generator;
531    
532        }
533    
534        /**
535         * Sets the item URL generator for ALL series and sends a
536         * {@link RendererChangeEvent} to all registered listeners.
537         *
538         * @param generator  the generator.
539         * 
540         * @deprecated This method should no longer be used (as of version 1.0.6). 
541         *     It is sufficient to rely on {@link #setSeriesItemURLGenerator(int, 
542         *     CategoryURLGenerator)} and 
543         *     {@link #setBaseItemURLGenerator(CategoryURLGenerator)}.
544         */
545        public void setItemURLGenerator(CategoryURLGenerator generator) {
546            this.itemURLGenerator = generator;
547            fireChangeEvent();
548        }
549    
550        /**
551         * Sets the URL generator for a series and sends a
552         * {@link RendererChangeEvent} to all registered listeners.
553         *
554         * @param series  the series index (zero based).
555         * @param generator  the generator.
556         *
557         * @see #getSeriesItemURLGenerator(int)
558         */
559        public void setSeriesItemURLGenerator(int series,
560                                              CategoryURLGenerator generator) {
561            this.itemURLGeneratorList.set(series, generator);
562            fireChangeEvent();
563        }
564    
565        /**
566         * Returns the base item URL generator.
567         *
568         * @return The item URL generator.
569         *
570         * @see #setBaseItemURLGenerator(CategoryURLGenerator)
571         */
572        public CategoryURLGenerator getBaseItemURLGenerator() {
573            return this.baseItemURLGenerator;
574        }
575    
576        /**
577         * Sets the base item URL generator and sends a
578         * {@link RendererChangeEvent} to all registered listeners.
579         *
580         * @param generator  the item URL generator (<code>null</code> permitted).
581         *
582         * @see #getBaseItemURLGenerator()
583         */
584        public void setBaseItemURLGenerator(CategoryURLGenerator generator) {
585            this.baseItemURLGenerator = generator;
586            fireChangeEvent();
587        }
588    
589        /**
590         * Returns the number of rows in the dataset.  This value is updated in the
591         * {@link AbstractCategoryItemRenderer#initialise} method.
592         *
593         * @return The row count.
594         */
595        public int getRowCount() {
596            return this.rowCount;
597        }
598    
599        /**
600         * Returns the number of columns in the dataset.  This value is updated in
601         * the {@link AbstractCategoryItemRenderer#initialise} method.
602         *
603         * @return The column count.
604         */
605        public int getColumnCount() {
606            return this.columnCount;
607        }
608    
609        /**
610         * Creates a new state instance---this method is called from the
611         * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int,
612         * PlotRenderingInfo)} method.  Subclasses can override this method if
613         * they need to use a subclass of {@link CategoryItemRendererState}.
614         *
615         * @param info  collects plot rendering info (<code>null</code> permitted).
616         *
617         * @return The new state instance (never <code>null</code>).
618         *
619         * @since 1.0.5
620         */
621        protected CategoryItemRendererState createState(PlotRenderingInfo info) {
622            return new CategoryItemRendererState(info);
623        }
624    
625        /**
626         * Initialises the renderer and returns a state object that will be used
627         * for the remainder of the drawing process for a single chart.  The state
628         * object allows for the fact that the renderer may be used simultaneously
629         * by multiple threads (each thread will work with a separate state object).
630         *
631         * @param g2  the graphics device.
632         * @param dataArea  the data area.
633         * @param plot  the plot.
634         * @param rendererIndex  the renderer index.
635         * @param info  an object for returning information about the structure of
636         *              the plot (<code>null</code> permitted).
637         *
638         * @return The renderer state.
639         */
640        public CategoryItemRendererState initialise(Graphics2D g2,
641                                                    Rectangle2D dataArea,
642                                                    CategoryPlot plot,
643                                                    int rendererIndex,
644                                                    PlotRenderingInfo info) {
645    
646            setPlot(plot);
647            CategoryDataset data = plot.getDataset(rendererIndex);
648            if (data != null) {
649                this.rowCount = data.getRowCount();
650                this.columnCount = data.getColumnCount();
651            }
652            else {
653                this.rowCount = 0;
654                this.columnCount = 0;
655            }
656            return createState(info);
657    
658        }
659    
660        /**
661         * Returns the range of values the renderer requires to display all the
662         * items from the specified dataset.
663         *
664         * @param dataset  the dataset (<code>null</code> permitted).
665         *
666         * @return The range (or <code>null</code> if the dataset is
667         *         <code>null</code> or empty).
668         */
669        public Range findRangeBounds(CategoryDataset dataset) {
670            return DatasetUtilities.findRangeBounds(dataset);
671        }
672    
673        /**
674         * Draws a background for the data area.  The default implementation just
675         * gets the plot to draw the background, but some renderers will override 
676         * this behaviour.
677         *
678         * @param g2  the graphics device.
679         * @param plot  the plot.
680         * @param dataArea  the data area.
681         */
682        public void drawBackground(Graphics2D g2,
683                                   CategoryPlot plot,
684                                   Rectangle2D dataArea) {
685    
686            plot.drawBackground(g2, dataArea);
687    
688        }
689    
690        /**
691         * Draws an outline for the data area.  The default implementation just
692         * gets the plot to draw the outline, but some renderers will override this
693         * behaviour.
694         *
695         * @param g2  the graphics device.
696         * @param plot  the plot.
697         * @param dataArea  the data area.
698         */
699        public void drawOutline(Graphics2D g2,
700                                CategoryPlot plot,
701                                Rectangle2D dataArea) {
702    
703            plot.drawOutline(g2, dataArea);
704    
705        }
706    
707        /**
708         * Draws a grid line against the domain axis.
709         * <P>
710         * Note that this default implementation assumes that the horizontal axis
711         * is the domain axis. If this is not the case, you will need to override
712         * this method.
713         *
714         * @param g2  the graphics device.
715         * @param plot  the plot.
716         * @param dataArea  the area for plotting data (not yet adjusted for any
717         *                  3D effect).
718         * @param value  the Java2D value at which the grid line should be drawn.
719         *
720         * @see #drawRangeGridline(Graphics2D, CategoryPlot, ValueAxis,
721         *     Rectangle2D, double)
722         */
723        public void drawDomainGridline(Graphics2D g2,
724                                       CategoryPlot plot,
725                                       Rectangle2D dataArea,
726                                       double value) {
727    
728            Line2D line = null;
729            PlotOrientation orientation = plot.getOrientation();
730    
731            if (orientation == PlotOrientation.HORIZONTAL) {
732                line = new Line2D.Double(dataArea.getMinX(), value,
733                        dataArea.getMaxX(), value);
734            }
735            else if (orientation == PlotOrientation.VERTICAL) {
736                line = new Line2D.Double(value, dataArea.getMinY(), value,
737                        dataArea.getMaxY());
738            }
739    
740            Paint paint = plot.getDomainGridlinePaint();
741            if (paint == null) {
742                paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT;
743            }
744            g2.setPaint(paint);
745    
746            Stroke stroke = plot.getDomainGridlineStroke();
747            if (stroke == null) {
748                stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE;
749            }
750            g2.setStroke(stroke);
751    
752            g2.draw(line);
753    
754        }
755    
756        /**
757         * Draws a grid line against the range axis.
758         *
759         * @param g2  the graphics device.
760         * @param plot  the plot.
761         * @param axis  the value axis.
762         * @param dataArea  the area for plotting data (not yet adjusted for any
763         *                  3D effect).
764         * @param value  the value at which the grid line should be drawn.
765         *
766         * @see #drawDomainGridline(Graphics2D, CategoryPlot, Rectangle2D, double)
767         *
768         */
769        public void drawRangeGridline(Graphics2D g2,
770                                      CategoryPlot plot,
771                                      ValueAxis axis,
772                                      Rectangle2D dataArea,
773                                      double value) {
774    
775            Range range = axis.getRange();
776            if (!range.contains(value)) {
777                return;
778            }
779    
780            PlotOrientation orientation = plot.getOrientation();
781            double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge());
782            Line2D line = null;
783            if (orientation == PlotOrientation.HORIZONTAL) {
784                line = new Line2D.Double(v, dataArea.getMinY(), v,
785                        dataArea.getMaxY());
786            }
787            else if (orientation == PlotOrientation.VERTICAL) {
788                line = new Line2D.Double(dataArea.getMinX(), v,
789                        dataArea.getMaxX(), v);
790            }
791    
792            Paint paint = plot.getRangeGridlinePaint();
793            if (paint == null) {
794                paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT;
795            }
796            g2.setPaint(paint);
797    
798            Stroke stroke = plot.getRangeGridlineStroke();
799            if (stroke == null) {
800                stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE;
801            }
802            g2.setStroke(stroke);
803    
804            g2.draw(line);
805    
806        }
807    
808        /**
809         * Draws a marker for the domain axis.
810         *
811         * @param g2  the graphics device (not <code>null</code>).
812         * @param plot  the plot (not <code>null</code>).
813         * @param axis  the range axis (not <code>null</code>).
814         * @param marker  the marker to be drawn (not <code>null</code>).
815         * @param dataArea  the area inside the axes (not <code>null</code>).
816         *
817         * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker,
818         *     Rectangle2D)
819         */
820        public void drawDomainMarker(Graphics2D g2,
821                                     CategoryPlot plot,
822                                     CategoryAxis axis,
823                                     CategoryMarker marker,
824                                     Rectangle2D dataArea) {
825    
826            Comparable category = marker.getKey();
827            CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this));
828            int columnIndex = dataset.getColumnIndex(category);
829            if (columnIndex < 0) {
830                return;
831            }
832    
833            final Composite savedComposite = g2.getComposite();
834            g2.setComposite(AlphaComposite.getInstance(
835                    AlphaComposite.SRC_OVER, marker.getAlpha()));
836    
837            PlotOrientation orientation = plot.getOrientation();
838            Rectangle2D bounds = null;
839            if (marker.getDrawAsLine()) {
840                double v = axis.getCategoryMiddle(columnIndex,
841                        dataset.getColumnCount(), dataArea,
842                        plot.getDomainAxisEdge());
843                Line2D line = null;
844                if (orientation == PlotOrientation.HORIZONTAL) {
845                    line = new Line2D.Double(dataArea.getMinX(), v,
846                            dataArea.getMaxX(), v);
847                }
848                else if (orientation == PlotOrientation.VERTICAL) {
849                    line = new Line2D.Double(v, dataArea.getMinY(), v,
850                            dataArea.getMaxY());
851                }
852                g2.setPaint(marker.getPaint());
853                g2.setStroke(marker.getStroke());
854                g2.draw(line);
855                bounds = line.getBounds2D();
856            }
857            else {
858                double v0 = axis.getCategoryStart(columnIndex,
859                        dataset.getColumnCount(), dataArea,
860                        plot.getDomainAxisEdge());
861                double v1 = axis.getCategoryEnd(columnIndex,
862                        dataset.getColumnCount(), dataArea,
863                        plot.getDomainAxisEdge());
864                Rectangle2D area = null;
865                if (orientation == PlotOrientation.HORIZONTAL) {
866                    area = new Rectangle2D.Double(dataArea.getMinX(), v0,
867                            dataArea.getWidth(), (v1 - v0));
868                }
869                else if (orientation == PlotOrientation.VERTICAL) {
870                    area = new Rectangle2D.Double(v0, dataArea.getMinY(),
871                            (v1 - v0), dataArea.getHeight());
872                }
873                g2.setPaint(marker.getPaint());
874                g2.fill(area);
875                bounds = area;
876            }
877    
878            String label = marker.getLabel();
879            RectangleAnchor anchor = marker.getLabelAnchor();
880            if (label != null) {
881                Font labelFont = marker.getLabelFont();
882                g2.setFont(labelFont);
883                g2.setPaint(marker.getLabelPaint());
884                Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
885                        g2, orientation, dataArea, bounds, marker.getLabelOffset(),
886                        marker.getLabelOffsetType(), anchor);
887                TextUtilities.drawAlignedString(label, g2,
888                        (float) coordinates.getX(), (float) coordinates.getY(),
889                        marker.getLabelTextAnchor());
890            }
891            g2.setComposite(savedComposite);
892        }
893    
894        /**
895         * Draws a marker for the range axis.
896         *
897         * @param g2  the graphics device (not <code>null</code>).
898         * @param plot  the plot (not <code>null</code>).
899         * @param axis  the range axis (not <code>null</code>).
900         * @param marker  the marker to be drawn (not <code>null</code>).
901         * @param dataArea  the area inside the axes (not <code>null</code>).
902         *
903         * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis,
904         *     CategoryMarker, Rectangle2D)
905         */
906        public void drawRangeMarker(Graphics2D g2,
907                                    CategoryPlot plot,
908                                    ValueAxis axis,
909                                    Marker marker,
910                                    Rectangle2D dataArea) {
911    
912            if (marker instanceof ValueMarker) {
913                ValueMarker vm = (ValueMarker) marker;
914                double value = vm.getValue();
915                Range range = axis.getRange();
916    
917                if (!range.contains(value)) {
918                    return;
919                }
920    
921                final Composite savedComposite = g2.getComposite();
922                g2.setComposite(AlphaComposite.getInstance(
923                        AlphaComposite.SRC_OVER, marker.getAlpha()));
924    
925                PlotOrientation orientation = plot.getOrientation();
926                double v = axis.valueToJava2D(value, dataArea,
927                        plot.getRangeAxisEdge());
928                Line2D line = null;
929                if (orientation == PlotOrientation.HORIZONTAL) {
930                    line = new Line2D.Double(v, dataArea.getMinY(), v,
931                            dataArea.getMaxY());
932                }
933                else if (orientation == PlotOrientation.VERTICAL) {
934                    line = new Line2D.Double(dataArea.getMinX(), v,
935                            dataArea.getMaxX(), v);
936                }
937    
938                g2.setPaint(marker.getPaint());
939                g2.setStroke(marker.getStroke());
940                g2.draw(line);
941    
942                String label = marker.getLabel();
943                RectangleAnchor anchor = marker.getLabelAnchor();
944                if (label != null) {
945                    Font labelFont = marker.getLabelFont();
946                    g2.setFont(labelFont);
947                    g2.setPaint(marker.getLabelPaint());
948                    Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
949                            g2, orientation, dataArea, line.getBounds2D(),
950                            marker.getLabelOffset(), LengthAdjustmentType.EXPAND,
951                            anchor);
952                    TextUtilities.drawAlignedString(label, g2,
953                            (float) coordinates.getX(), (float) coordinates.getY(),
954                            marker.getLabelTextAnchor());
955                }
956                g2.setComposite(savedComposite);
957            }
958            else if (marker instanceof IntervalMarker) {
959                IntervalMarker im = (IntervalMarker) marker;
960                double start = im.getStartValue();
961                double end = im.getEndValue();
962                Range range = axis.getRange();
963                if (!(range.intersects(start, end))) {
964                    return;
965                }
966    
967                final Composite savedComposite = g2.getComposite();
968                g2.setComposite(AlphaComposite.getInstance(
969                        AlphaComposite.SRC_OVER, marker.getAlpha()));
970    
971                double start2d = axis.valueToJava2D(start, dataArea,
972                        plot.getRangeAxisEdge());
973                double end2d = axis.valueToJava2D(end, dataArea,
974                        plot.getRangeAxisEdge());
975                double low = Math.min(start2d, end2d);
976                double high = Math.max(start2d, end2d);
977    
978                PlotOrientation orientation = plot.getOrientation();
979                Rectangle2D rect = null;
980                if (orientation == PlotOrientation.HORIZONTAL) {
981                    // clip left and right bounds to data area
982                    low = Math.max(low, dataArea.getMinX());
983                    high = Math.min(high, dataArea.getMaxX());
984                    rect = new Rectangle2D.Double(low,
985                            dataArea.getMinY(), high - low,
986                            dataArea.getHeight());
987                }
988                else if (orientation == PlotOrientation.VERTICAL) {
989                    // clip top and bottom bounds to data area
990                    low = Math.max(low, dataArea.getMinY());
991                    high = Math.min(high, dataArea.getMaxY());
992                    rect = new Rectangle2D.Double(dataArea.getMinX(),
993                            low, dataArea.getWidth(),
994                            high - low);
995                }
996                Paint p = marker.getPaint();
997                if (p instanceof GradientPaint) {
998                    GradientPaint gp = (GradientPaint) p;
999                    GradientPaintTransformer t = im.getGradientPaintTransformer();
1000                    if (t != null) {
1001                        gp = t.transform(gp, rect);
1002                    }
1003                    g2.setPaint(gp);
1004                }
1005                else {
1006                    g2.setPaint(p);
1007                }
1008                g2.fill(rect);
1009    
1010                // now draw the outlines, if visible...
1011                if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
1012                    if (orientation == PlotOrientation.VERTICAL) {
1013                        Line2D line = new Line2D.Double();
1014                        double x0 = dataArea.getMinX();
1015                        double x1 = dataArea.getMaxX();
1016                        g2.setPaint(im.getOutlinePaint());
1017                        g2.setStroke(im.getOutlineStroke());
1018                        if (range.contains(start)) {
1019                            line.setLine(x0, start2d, x1, start2d);
1020                            g2.draw(line);
1021                        }
1022                        if (range.contains(end)) {
1023                            line.setLine(x0, end2d, x1, end2d);
1024                            g2.draw(line);
1025                        }
1026                    }
1027                    else { // PlotOrientation.HORIZONTAL
1028                        Line2D line = new Line2D.Double();
1029                        double y0 = dataArea.getMinY();
1030                        double y1 = dataArea.getMaxY();
1031                        g2.setPaint(im.getOutlinePaint());
1032                        g2.setStroke(im.getOutlineStroke());
1033                        if (range.contains(start)) {
1034                            line.setLine(start2d, y0, start2d, y1);
1035                            g2.draw(line);
1036                        }
1037                        if (range.contains(end)) {
1038                            line.setLine(end2d, y0, end2d, y1);
1039                            g2.draw(line);
1040                        }
1041                    }
1042                }
1043    
1044                String label = marker.getLabel();
1045                RectangleAnchor anchor = marker.getLabelAnchor();
1046                if (label != null) {
1047                    Font labelFont = marker.getLabelFont();
1048                    g2.setFont(labelFont);
1049                    g2.setPaint(marker.getLabelPaint());
1050                    Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1051                            g2, orientation, dataArea, rect,
1052                            marker.getLabelOffset(), marker.getLabelOffsetType(),
1053                            anchor);
1054                    TextUtilities.drawAlignedString(label, g2,
1055                            (float) coordinates.getX(), (float) coordinates.getY(),
1056                            marker.getLabelTextAnchor());
1057                }
1058                g2.setComposite(savedComposite);
1059            }
1060        }
1061    
1062        /**
1063         * Calculates the (x, y) coordinates for drawing the label for a marker on
1064         * the range axis.
1065         *
1066         * @param g2  the graphics device.
1067         * @param orientation  the plot orientation.
1068         * @param dataArea  the data area.
1069         * @param markerArea  the rectangle surrounding the marker.
1070         * @param markerOffset  the marker offset.
1071         * @param labelOffsetType  the label offset type.
1072         * @param anchor  the label anchor.
1073         *
1074         * @return The coordinates for drawing the marker label.
1075         */
1076        protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
1077                                          PlotOrientation orientation,
1078                                          Rectangle2D dataArea,
1079                                          Rectangle2D markerArea,
1080                                          RectangleInsets markerOffset,
1081                                          LengthAdjustmentType labelOffsetType,
1082                                          RectangleAnchor anchor) {
1083    
1084            Rectangle2D anchorRect = null;
1085            if (orientation == PlotOrientation.HORIZONTAL) {
1086                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1087                        LengthAdjustmentType.CONTRACT, labelOffsetType);
1088            }
1089            else if (orientation == PlotOrientation.VERTICAL) {
1090                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1091                        labelOffsetType, LengthAdjustmentType.CONTRACT);
1092            }
1093            return RectangleAnchor.coordinates(anchorRect, anchor);
1094    
1095        }
1096    
1097        /**
1098         * Calculates the (x, y) coordinates for drawing a marker label.
1099         *
1100         * @param g2  the graphics device.
1101         * @param orientation  the plot orientation.
1102         * @param dataArea  the data area.
1103         * @param markerArea  the rectangle surrounding the marker.
1104         * @param markerOffset  the marker offset.
1105         * @param labelOffsetType  the label offset type.
1106         * @param anchor  the label anchor.
1107         *
1108         * @return The coordinates for drawing the marker label.
1109         */
1110        protected Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2,
1111                                          PlotOrientation orientation,
1112                                          Rectangle2D dataArea,
1113                                          Rectangle2D markerArea,
1114                                          RectangleInsets markerOffset,
1115                                          LengthAdjustmentType labelOffsetType,
1116                                          RectangleAnchor anchor) {
1117    
1118            Rectangle2D anchorRect = null;
1119            if (orientation == PlotOrientation.HORIZONTAL) {
1120                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1121                        labelOffsetType, LengthAdjustmentType.CONTRACT);
1122            }
1123            else if (orientation == PlotOrientation.VERTICAL) {
1124                anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1125                        LengthAdjustmentType.CONTRACT, labelOffsetType);
1126            }
1127            return RectangleAnchor.coordinates(anchorRect, anchor);
1128    
1129        }
1130    
1131        /**
1132         * Returns a legend item for a series.  This default implementation will
1133         * return <code>null</code> if {@link #isSeriesVisible(int)} or 
1134         * {@link #isSeriesVisibleInLegend(int)} returns <code>false</code>.
1135         *
1136         * @param datasetIndex  the dataset index (zero-based).
1137         * @param series  the series index (zero-based).
1138         *
1139         * @return The legend item (possibly <code>null</code>).
1140         *
1141         * @see #getLegendItems()
1142         */
1143        public LegendItem getLegendItem(int datasetIndex, int series) {
1144    
1145            CategoryPlot p = getPlot();
1146            if (p == null) {
1147                return null;
1148            }
1149    
1150            // check that a legend item needs to be displayed...
1151            if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) {
1152                return null;
1153            }
1154    
1155            CategoryDataset dataset = p.getDataset(datasetIndex);
1156            String label = this.legendItemLabelGenerator.generateLabel(dataset,
1157                    series);
1158            String description = label;
1159            String toolTipText = null;
1160            if (this.legendItemToolTipGenerator != null) {
1161                toolTipText = this.legendItemToolTipGenerator.generateLabel(
1162                        dataset, series);
1163            }
1164            String urlText = null;
1165            if (this.legendItemURLGenerator != null) {
1166                urlText = this.legendItemURLGenerator.generateLabel(dataset,
1167                        series);
1168            }
1169            Shape shape = lookupSeriesShape(series);
1170            Paint paint = lookupSeriesPaint(series);
1171            Paint outlinePaint = lookupSeriesOutlinePaint(series);
1172            Stroke outlineStroke = lookupSeriesOutlineStroke(series);
1173    
1174            LegendItem item = new LegendItem(label, description, toolTipText,
1175                    urlText, shape, paint, outlineStroke, outlinePaint);
1176            item.setSeriesKey(dataset.getRowKey(series));
1177            item.setSeriesIndex(series);
1178            item.setDataset(dataset);
1179            item.setDatasetIndex(datasetIndex);
1180            return item;
1181        }
1182    
1183        /**
1184         * Tests this renderer for equality with another object.
1185         *
1186         * @param obj  the object.
1187         *
1188         * @return <code>true</code> or <code>false</code>.
1189         */
1190        public boolean equals(Object obj) {
1191    
1192            if (obj == this) {
1193                return true;
1194            }
1195            if (!(obj instanceof AbstractCategoryItemRenderer)) {
1196                return false;
1197            }
1198            AbstractCategoryItemRenderer that = (AbstractCategoryItemRenderer) obj;
1199    
1200            if (!ObjectUtilities.equal(this.itemLabelGenerator,
1201                    that.itemLabelGenerator)) {
1202                return false;
1203            }
1204            if (!ObjectUtilities.equal(this.itemLabelGeneratorList,
1205                    that.itemLabelGeneratorList)) {
1206                return false;
1207            }
1208            if (!ObjectUtilities.equal(this.baseItemLabelGenerator,
1209                    that.baseItemLabelGenerator)) {
1210                return false;
1211            }
1212            if (!ObjectUtilities.equal(this.toolTipGenerator,
1213                    that.toolTipGenerator)) {
1214                return false;
1215            }
1216            if (!ObjectUtilities.equal(this.toolTipGeneratorList,
1217                    that.toolTipGeneratorList)) {
1218                return false;
1219            }
1220            if (!ObjectUtilities.equal(this.baseToolTipGenerator,
1221                    that.baseToolTipGenerator)) {
1222                return false;
1223            }
1224            if (!ObjectUtilities.equal(this.itemURLGenerator,
1225                    that.itemURLGenerator)) {
1226                return false;
1227            }
1228            if (!ObjectUtilities.equal(this.itemURLGeneratorList,
1229                    that.itemURLGeneratorList)) {
1230                return false;
1231            }
1232            if (!ObjectUtilities.equal(this.baseItemURLGenerator,
1233                    that.baseItemURLGenerator)) {
1234                return false;
1235            }
1236            if (!ObjectUtilities.equal(this.legendItemLabelGenerator,
1237                    that.legendItemLabelGenerator)) {
1238                return false;
1239            }
1240            if (!ObjectUtilities.equal(this.legendItemToolTipGenerator,
1241                    that.legendItemToolTipGenerator)) {
1242                return false;
1243            }
1244            if (!ObjectUtilities.equal(this.legendItemURLGenerator,
1245                    that.legendItemURLGenerator)) {
1246                return false;
1247            }
1248            return super.equals(obj);
1249        }
1250    
1251        /**
1252         * Returns a hash code for the renderer.
1253         *
1254         * @return The hash code.
1255         */
1256        public int hashCode() {
1257            int result = super.hashCode();
1258            return result;
1259        }
1260    
1261        /**
1262         * Returns the drawing supplier from the plot.
1263         *
1264         * @return The drawing supplier (possibly <code>null</code>).
1265         */
1266        public DrawingSupplier getDrawingSupplier() {
1267            DrawingSupplier result = null;
1268            CategoryPlot cp = getPlot();
1269            if (cp != null) {
1270                result = cp.getDrawingSupplier();
1271            }
1272            return result;
1273        }
1274    
1275        /**
1276         * Draws an item label.
1277         *
1278         * @param g2  the graphics device.
1279         * @param orientation  the orientation.
1280         * @param dataset  the dataset.
1281         * @param row  the row.
1282         * @param column  the column.
1283         * @param x  the x coordinate (in Java2D space).
1284         * @param y  the y coordinate (in Java2D space).
1285         * @param negative  indicates a negative value (which affects the item
1286         *                  label position).
1287         */
1288        protected void drawItemLabel(Graphics2D g2,
1289                                     PlotOrientation orientation,
1290                                     CategoryDataset dataset,
1291                                     int row, int column,
1292                                     double x, double y,
1293                                     boolean negative) {
1294    
1295            CategoryItemLabelGenerator generator
1296                = getItemLabelGenerator(row, column);
1297            if (generator != null) {
1298                Font labelFont = getItemLabelFont(row, column);
1299                Paint paint = getItemLabelPaint(row, column);
1300                g2.setFont(labelFont);
1301                g2.setPaint(paint);
1302                String label = generator.generateLabel(dataset, row, column);
1303                ItemLabelPosition position = null;
1304                if (!negative) {
1305                    position = getPositiveItemLabelPosition(row, column);
1306                }
1307                else {
1308                    position = getNegativeItemLabelPosition(row, column);
1309                }
1310                Point2D anchorPoint = calculateLabelAnchorPoint(
1311                        position.getItemLabelAnchor(), x, y, orientation);
1312                TextUtilities.drawRotatedString(label, g2,
1313                        (float) anchorPoint.getX(), (float) anchorPoint.getY(),
1314                        position.getTextAnchor(),
1315                        position.getAngle(), position.getRotationAnchor());
1316            }
1317    
1318        }
1319    
1320        /**
1321         * Returns an independent copy of the renderer.  The <code>plot</code>
1322         * reference is shallow copied.
1323         *
1324         * @return A clone.
1325         *
1326         * @throws CloneNotSupportedException  can be thrown if one of the objects
1327         *         belonging to the renderer does not support cloning (for example,
1328         *         an item label generator).
1329         */
1330        public Object clone() throws CloneNotSupportedException {
1331    
1332            AbstractCategoryItemRenderer clone
1333                = (AbstractCategoryItemRenderer) super.clone();
1334    
1335            if (this.itemLabelGenerator != null) {
1336                if (this.itemLabelGenerator instanceof PublicCloneable) {
1337                    PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator;
1338                    clone.itemLabelGenerator
1339                            = (CategoryItemLabelGenerator) pc.clone();
1340                }
1341                else {
1342                    throw new CloneNotSupportedException(
1343                            "ItemLabelGenerator not cloneable.");
1344                }
1345            }
1346    
1347            if (this.itemLabelGeneratorList != null) {
1348                clone.itemLabelGeneratorList
1349                        = (ObjectList) this.itemLabelGeneratorList.clone();
1350            }
1351    
1352            if (this.baseItemLabelGenerator != null) {
1353                if (this.baseItemLabelGenerator instanceof PublicCloneable) {
1354                    PublicCloneable pc
1355                            = (PublicCloneable) this.baseItemLabelGenerator;
1356                    clone.baseItemLabelGenerator
1357                            = (CategoryItemLabelGenerator) pc.clone();
1358                }
1359                else {
1360                    throw new CloneNotSupportedException(
1361                            "ItemLabelGenerator not cloneable.");
1362                }
1363            }
1364    
1365            if (this.toolTipGenerator != null) {
1366                if (this.toolTipGenerator instanceof PublicCloneable) {
1367                    PublicCloneable pc = (PublicCloneable) this.toolTipGenerator;
1368                    clone.toolTipGenerator = (CategoryToolTipGenerator) pc.clone();
1369                }
1370                else {
1371                    throw new CloneNotSupportedException(
1372                            "Tool tip generator not cloneable.");
1373                }
1374            }
1375    
1376            if (this.toolTipGeneratorList != null) {
1377                clone.toolTipGeneratorList
1378                        = (ObjectList) this.toolTipGeneratorList.clone();
1379            }
1380    
1381            if (this.baseToolTipGenerator != null) {
1382                if (this.baseToolTipGenerator instanceof PublicCloneable) {
1383                    PublicCloneable pc
1384                            = (PublicCloneable) this.baseToolTipGenerator;
1385                    clone.baseToolTipGenerator
1386                            = (CategoryToolTipGenerator) pc.clone();
1387                }
1388                else {
1389                    throw new CloneNotSupportedException(
1390                            "Base tool tip generator not cloneable.");
1391                }
1392            }
1393    
1394            if (this.itemURLGenerator != null) {
1395                if (this.itemURLGenerator instanceof PublicCloneable) {
1396                    PublicCloneable pc = (PublicCloneable) this.itemURLGenerator;
1397                    clone.itemURLGenerator = (CategoryURLGenerator) pc.clone();
1398                }
1399                else {
1400                    throw new CloneNotSupportedException(
1401                            "Item URL generator not cloneable.");
1402                }
1403            }
1404    
1405            if (this.itemURLGeneratorList != null) {
1406                clone.itemURLGeneratorList
1407                        = (ObjectList) this.itemURLGeneratorList.clone();
1408            }
1409    
1410            if (this.baseItemURLGenerator != null) {
1411                if (this.baseItemURLGenerator instanceof PublicCloneable) {
1412                    PublicCloneable pc
1413                            = (PublicCloneable) this.baseItemURLGenerator;
1414                    clone.baseItemURLGenerator = (CategoryURLGenerator) pc.clone();
1415                }
1416                else {
1417                    throw new CloneNotSupportedException(
1418                            "Base item URL generator not cloneable.");
1419                }
1420            }
1421    
1422            if (this.legendItemLabelGenerator instanceof PublicCloneable) {
1423                clone.legendItemLabelGenerator = (CategorySeriesLabelGenerator)
1424                        ObjectUtilities.clone(this.legendItemLabelGenerator);
1425            }
1426            if (this.legendItemToolTipGenerator instanceof PublicCloneable) {
1427                clone.legendItemToolTipGenerator = (CategorySeriesLabelGenerator)
1428                        ObjectUtilities.clone(this.legendItemToolTipGenerator);
1429            }
1430            if (this.legendItemURLGenerator instanceof PublicCloneable) {
1431                clone.legendItemURLGenerator = (CategorySeriesLabelGenerator)
1432                        ObjectUtilities.clone(this.legendItemURLGenerator);
1433            }
1434            return clone;
1435        }
1436    
1437        /**
1438         * Returns a domain axis for a plot.
1439         *
1440         * @param plot  the plot.
1441         * @param index  the axis index.
1442         *
1443         * @return A domain axis.
1444         */
1445        protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) {
1446            CategoryAxis result = plot.getDomainAxis(index);
1447            if (result == null) {
1448                result = plot.getDomainAxis();
1449            }
1450            return result;
1451        }
1452    
1453        /**
1454         * Returns a range axis for a plot.
1455         *
1456         * @param plot  the plot.
1457         * @param index  the axis index.
1458         *
1459         * @return A range axis.
1460         */
1461        protected ValueAxis getRangeAxis(CategoryPlot plot, int index) {
1462            ValueAxis result = plot.getRangeAxis(index);
1463            if (result == null) {
1464                result = plot.getRangeAxis();
1465            }
1466            return result;
1467        }
1468    
1469        /**
1470         * Returns a (possibly empty) collection of legend items for the series
1471         * that this renderer is responsible for drawing.
1472         *
1473         * @return The legend item collection (never <code>null</code>).
1474         *
1475         * @see #getLegendItem(int, int)
1476         */
1477        public LegendItemCollection getLegendItems() {
1478            if (this.plot == null) {
1479                return new LegendItemCollection();
1480            }
1481            LegendItemCollection result = new LegendItemCollection();
1482            int index = this.plot.getIndexOf(this);
1483            CategoryDataset dataset = this.plot.getDataset(index);
1484            if (dataset != null) {
1485                int seriesCount = dataset.getRowCount();
1486                for (int i = 0; i < seriesCount; i++) {
1487                    if (isSeriesVisibleInLegend(i)) {
1488                        LegendItem item = getLegendItem(index, i);
1489                        if (item != null) {
1490                            result.add(item);
1491                        }
1492                    }
1493                }
1494    
1495            }
1496            return result;
1497        }
1498    
1499        /**
1500         * Returns the legend item label generator.
1501         *
1502         * @return The label generator (never <code>null</code>).
1503         *
1504         * @see #setLegendItemLabelGenerator(CategorySeriesLabelGenerator)
1505         */
1506        public CategorySeriesLabelGenerator getLegendItemLabelGenerator() {
1507            return this.legendItemLabelGenerator;
1508        }
1509    
1510        /**
1511         * Sets the legend item label generator and sends a
1512         * {@link RendererChangeEvent} to all registered listeners.
1513         *
1514         * @param generator  the generator (<code>null</code> not permitted).
1515         *
1516         * @see #getLegendItemLabelGenerator()
1517         */
1518        public void setLegendItemLabelGenerator(
1519                CategorySeriesLabelGenerator generator) {
1520            if (generator == null) {
1521                throw new IllegalArgumentException("Null 'generator' argument.");
1522            }
1523            this.legendItemLabelGenerator = generator;
1524            fireChangeEvent();
1525        }
1526    
1527        /**
1528         * Returns the legend item tool tip generator.
1529         *
1530         * @return The tool tip generator (possibly <code>null</code>).
1531         *
1532         * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator)
1533         */
1534        public CategorySeriesLabelGenerator getLegendItemToolTipGenerator() {
1535            return this.legendItemToolTipGenerator;
1536        }
1537    
1538        /**
1539         * Sets the legend item tool tip generator and sends a
1540         * {@link RendererChangeEvent} to all registered listeners.
1541         *
1542         * @param generator  the generator (<code>null</code> permitted).
1543         *
1544         * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator)
1545         */
1546        public void setLegendItemToolTipGenerator(
1547                CategorySeriesLabelGenerator generator) {
1548            this.legendItemToolTipGenerator = generator;
1549            fireChangeEvent();
1550        }
1551    
1552        /**
1553         * Returns the legend item URL generator.
1554         *
1555         * @return The URL generator (possibly <code>null</code>).
1556         *
1557         * @see #setLegendItemURLGenerator(CategorySeriesLabelGenerator)
1558         */
1559        public CategorySeriesLabelGenerator getLegendItemURLGenerator() {
1560            return this.legendItemURLGenerator;
1561        }
1562    
1563        /**
1564         * Sets the legend item URL generator and sends a
1565         * {@link RendererChangeEvent} to all registered listeners.
1566         *
1567         * @param generator  the generator (<code>null</code> permitted).
1568         *
1569         * @see #getLegendItemURLGenerator()
1570         */
1571        public void setLegendItemURLGenerator(
1572                CategorySeriesLabelGenerator generator) {
1573            this.legendItemURLGenerator = generator;
1574            fireChangeEvent();
1575        }
1576    
1577        /**
1578         * Adds an entity with the specified hotspot.
1579         *
1580         * @param entities  the entity collection.
1581         * @param dataset  the dataset.
1582         * @param row  the row index.
1583         * @param column  the column index.
1584         * @param hotspot  the hotspot.
1585         */
1586        protected void addItemEntity(EntityCollection entities,
1587                                     CategoryDataset dataset, int row, int column,
1588                                     Shape hotspot) {
1589    
1590            String tip = null;
1591            CategoryToolTipGenerator tipster = getToolTipGenerator(row, column);
1592            if (tipster != null) {
1593                tip = tipster.generateToolTip(dataset, row, column);
1594            }
1595            String url = null;
1596            CategoryURLGenerator urlster = getItemURLGenerator(row, column);
1597            if (urlster != null) {
1598                url = urlster.generateURL(dataset, row, column);
1599            }
1600            CategoryItemEntity entity = new CategoryItemEntity(hotspot, tip, url,
1601                    dataset, dataset.getRowKey(row), dataset.getColumnKey(column));
1602            entities.add(entity);
1603    
1604        }
1605    
1606    
1607    }