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