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 }