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