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