001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, 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 * CategoryPlot.java
029 * -----------------
030 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): Jeremy Bowman;
034 * Arnaud Lelievre;
035 * Richard West, Advanced Micro Devices, Inc.;
036 *
037 * Changes
038 * -------
039 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
040 * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
041 * 18-Sep-2001 : Updated header (DG);
042 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
043 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
044 * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
045 * available space rather than a fixed number of units (DG);
046 * 12-Dec-2001 : Changed constructors to protected (DG);
047 * 13-Dec-2001 : Added tooltips (DG);
048 * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
049 * some argument checking code. Thanks to Taoufik Romdhane for
050 * suggesting this (DG);
051 * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
052 * alpha-transparency for Plot and subclasses (DG);
053 * 06-Mar-2002 : Updated import statements (DG);
054 * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
055 * to use the CategoryItemRenderer interface (DG);
056 * 22-Mar-2002 : Dropped the getCategories() method (DG);
057 * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
058 * class (DG);
059 * 29-Apr-2002 : New methods to support printing values at the end of bars,
060 * contributed by Jeremy Bowman (DG);
061 * 11-May-2002 : New methods for label visibility and overlaid plot support,
062 * contributed by Jeremy Bowman (DG);
063 * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
064 * renderer. Moved constants into the CategoryPlotConstants
065 * interface. Updated Javadoc comments (DG);
066 * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
067 * lower bound on the range axis (if necessary), updated
068 * Javadocs (DG);
069 * 25-Jun-2002 : Removed redundant imports (DG);
070 * 20-Aug-2002 : Changed the constructor for Marker (DG);
071 * 28-Aug-2002 : Added listener notification to setDomainAxis() and
072 * setRangeAxis() (DG);
073 * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
074 * Checkstyle (DG);
075 * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
076 * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
077 * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
078 * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
079 * these were set in the axes) (DG);
080 * 19-Nov-2002 : Added axis location parameters to constructor (DG);
081 * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
082 * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
083 * 26-Mar-2003 : Implemented Serializable (DG);
084 * 02-May-2003 : Moved render() method up from subclasses. Added secondary
085 * range markers. Added an attribute to control the dataset
086 * rendering order. Added a drawAnnotations() method. Changed
087 * the axis location from an int to an AxisLocation (DG);
088 * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
089 * this class (DG);
090 * 02-Jun-2003 : Removed check for range axis compatibility (DG);
091 * 04-Jul-2003 : Added a domain gridline position attribute (DG);
092 * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
093 * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
094 * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
095 * changes) (DG);
096 * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
097 * 790407 (initialise method) (DG);
098 * 08-Sep-2003 : Added internationalization via use of properties
099 * resourceBundle (RFE 690236) (AL);
100 * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed
101 * ValueAxis API (DG);
102 * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
103 * 15-Sep-2003 : Fixed two bugs in serialization, implemented
104 * PublicCloneable (DG);
105 * 23-Oct-2003 : Added event notification for changes to renderer (DG);
106 * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
107 * 03-Dec-2003 : Modified draw method to accept anchor (DG);
108 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
109 * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
110 * stacked (DG);
111 * 12-May-2004 : Added fixed legend items (DG);
112 * 19-May-2004 : Added check for null legend item from renderer (DG);
113 * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
114 * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
115 * --> datasetsMappedToRangeAxis(), and ensured that returned
116 * list doesn't contain null datasets (DG);
117 * 12-Nov-2004 : Implemented new Zoomable interface (DG);
118 * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
119 * CategoryItemRenderer (DG);
120 * 04-May-2005 : Fixed serialization of range markers (DG);
121 * 05-May-2005 : Updated draw() method parameters (DG);
122 * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
123 * RFE 1183100 (DG);
124 * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
125 * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
126 * 02-Jun-2005 : Added support for domain markers (DG);
127 * 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG);
128 * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
129 * 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to
130 * match XYPlot (see RFE 1220495) (DG);
131 * ------------- JFREECHART 1.0.x ---------------------------------------------
132 * 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the
133 * renderer might influence the axis range (DG);
134 * 27-Jan-2006 : Added various null argument checks (DG);
135 * 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing
136 * category labels, thanks to Adriaan Joubert (1277726) (DG);
137 * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
138 * 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and
139 * getCategoriesForAxis() methods (DG);
140 * 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and
141 * setRowRenderingOrder() (DG);
142 * 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data
143 * area) (DG);
144 * 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument
145 * ignored) (DG);
146 * 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and
147 * setRangeCrosshairStroke(), fixed clipping for
148 * annotations (DG);
149 * 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG);
150 * 10-Jul-2007 : Added getRangeAxisIndex(ValueAxis) method (DG);
151 * 24-Sep-2007 : Implemented new zoom methods (DG);
152 * 25-Oct-2007 : Added some argument checks (DG);
153 * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
154 * and range markers (DG);
155 * 14-Nov-2007 : Added missing event notifications (DG);
156 * 25-Mar-2008 : Added new methods with optional notification - see patch
157 * 1913751 (DG);
158 * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and
159 * removeRangeMarker() (DG);
160 * 23-Apr-2008 : Fixed equals() and clone() methods (DG);
161 * 26-Jun-2008 : Fixed crosshair support (DG);
162 * 10-Jul-2008 : Fixed outline visibility for 3D renderers (DG);
163 * 12-Aug-2008 : Added rendererCount() method (DG);
164 *
165 */
166
167 package org.jfree.chart.plot;
168
169 import java.awt.AlphaComposite;
170 import java.awt.BasicStroke;
171 import java.awt.Color;
172 import java.awt.Composite;
173 import java.awt.Font;
174 import java.awt.Graphics2D;
175 import java.awt.Paint;
176 import java.awt.Shape;
177 import java.awt.Stroke;
178 import java.awt.geom.Line2D;
179 import java.awt.geom.Point2D;
180 import java.awt.geom.Rectangle2D;
181 import java.io.IOException;
182 import java.io.ObjectInputStream;
183 import java.io.ObjectOutputStream;
184 import java.io.Serializable;
185 import java.util.ArrayList;
186 import java.util.Collection;
187 import java.util.Collections;
188 import java.util.HashMap;
189 import java.util.Iterator;
190 import java.util.List;
191 import java.util.Map;
192 import java.util.ResourceBundle;
193 import java.util.Set;
194
195 import org.jfree.chart.LegendItem;
196 import org.jfree.chart.LegendItemCollection;
197 import org.jfree.chart.annotations.CategoryAnnotation;
198 import org.jfree.chart.axis.Axis;
199 import org.jfree.chart.axis.AxisCollection;
200 import org.jfree.chart.axis.AxisLocation;
201 import org.jfree.chart.axis.AxisSpace;
202 import org.jfree.chart.axis.AxisState;
203 import org.jfree.chart.axis.CategoryAnchor;
204 import org.jfree.chart.axis.CategoryAxis;
205 import org.jfree.chart.axis.ValueAxis;
206 import org.jfree.chart.axis.ValueTick;
207 import org.jfree.chart.event.ChartChangeEventType;
208 import org.jfree.chart.event.PlotChangeEvent;
209 import org.jfree.chart.event.RendererChangeEvent;
210 import org.jfree.chart.event.RendererChangeListener;
211 import org.jfree.chart.renderer.category.CategoryItemRenderer;
212 import org.jfree.chart.renderer.category.CategoryItemRendererState;
213 import org.jfree.data.Range;
214 import org.jfree.data.category.CategoryDataset;
215 import org.jfree.data.general.Dataset;
216 import org.jfree.data.general.DatasetChangeEvent;
217 import org.jfree.data.general.DatasetUtilities;
218 import org.jfree.io.SerialUtilities;
219 import org.jfree.ui.Layer;
220 import org.jfree.ui.RectangleEdge;
221 import org.jfree.ui.RectangleInsets;
222 import org.jfree.util.ObjectList;
223 import org.jfree.util.ObjectUtilities;
224 import org.jfree.util.PaintUtilities;
225 import org.jfree.util.PublicCloneable;
226 import org.jfree.util.ShapeUtilities;
227 import org.jfree.util.SortOrder;
228
229 /**
230 * A general plotting class that uses data from a {@link CategoryDataset} and
231 * renders each data item using a {@link CategoryItemRenderer}.
232 */
233 public class CategoryPlot extends Plot implements ValueAxisPlot,
234 Zoomable, RendererChangeListener, Cloneable, PublicCloneable,
235 Serializable {
236
237 /** For serialization. */
238 private static final long serialVersionUID = -3537691700434728188L;
239
240 /**
241 * The default visibility of the grid lines plotted against the domain
242 * axis.
243 */
244 public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
245
246 /**
247 * The default visibility of the grid lines plotted against the range
248 * axis.
249 */
250 public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
251
252 /** The default grid line stroke. */
253 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
254 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[]
255 {2.0f, 2.0f}, 0.0f);
256
257 /** The default grid line paint. */
258 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
259
260 /** The default value label font. */
261 public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif",
262 Font.PLAIN, 10);
263
264 /**
265 * The default crosshair visibility.
266 *
267 * @since 1.0.5
268 */
269 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
270
271 /**
272 * The default crosshair stroke.
273 *
274 * @since 1.0.5
275 */
276 public static final Stroke DEFAULT_CROSSHAIR_STROKE
277 = DEFAULT_GRIDLINE_STROKE;
278
279 /**
280 * The default crosshair paint.
281 *
282 * @since 1.0.5
283 */
284 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
285
286 /** The resourceBundle for the localization. */
287 protected static ResourceBundle localizationResources
288 = ResourceBundle.getBundle(
289 "org.jfree.chart.plot.LocalizationBundle");
290
291 /** The plot orientation. */
292 private PlotOrientation orientation;
293
294 /** The offset between the data area and the axes. */
295 private RectangleInsets axisOffset;
296
297 /** Storage for the domain axes. */
298 private ObjectList domainAxes;
299
300 /** Storage for the domain axis locations. */
301 private ObjectList domainAxisLocations;
302
303 /**
304 * A flag that controls whether or not the shared domain axis is drawn
305 * (only relevant when the plot is being used as a subplot).
306 */
307 private boolean drawSharedDomainAxis;
308
309 /** Storage for the range axes. */
310 private ObjectList rangeAxes;
311
312 /** Storage for the range axis locations. */
313 private ObjectList rangeAxisLocations;
314
315 /** Storage for the datasets. */
316 private ObjectList datasets;
317
318 /** Storage for keys that map datasets to domain axes. */
319 private ObjectList datasetToDomainAxisMap;
320
321 /** Storage for keys that map datasets to range axes. */
322 private ObjectList datasetToRangeAxisMap;
323
324 /** Storage for the renderers. */
325 private ObjectList renderers;
326
327 /** The dataset rendering order. */
328 private DatasetRenderingOrder renderingOrder
329 = DatasetRenderingOrder.REVERSE;
330
331 /**
332 * Controls the order in which the columns are traversed when rendering the
333 * data items.
334 */
335 private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
336
337 /**
338 * Controls the order in which the rows are traversed when rendering the
339 * data items.
340 */
341 private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
342
343 /**
344 * A flag that controls whether the grid-lines for the domain axis are
345 * visible.
346 */
347 private boolean domainGridlinesVisible;
348
349 /** The position of the domain gridlines relative to the category. */
350 private CategoryAnchor domainGridlinePosition;
351
352 /** The stroke used to draw the domain grid-lines. */
353 private transient Stroke domainGridlineStroke;
354
355 /** The paint used to draw the domain grid-lines. */
356 private transient Paint domainGridlinePaint;
357
358 /**
359 * A flag that controls whether the grid-lines for the range axis are
360 * visible.
361 */
362 private boolean rangeGridlinesVisible;
363
364 /** The stroke used to draw the range axis grid-lines. */
365 private transient Stroke rangeGridlineStroke;
366
367 /** The paint used to draw the range axis grid-lines. */
368 private transient Paint rangeGridlinePaint;
369
370 /** The anchor value. */
371 private double anchorValue;
372
373 /**
374 * The index for the dataset that the crosshairs are linked to (this
375 * determines which axes the crosshairs are plotted against).
376 *
377 * @since 1.0.11
378 */
379 private int crosshairDatasetIndex;
380
381 /**
382 * A flag that controls the visibility of the domain crosshair.
383 *
384 * @since 1.0.11
385 */
386 private boolean domainCrosshairVisible;
387
388 /**
389 * The row key for the crosshair point.
390 *
391 * @since 1.0.11
392 */
393 private Comparable domainCrosshairRowKey;
394
395 /**
396 * The column key for the crosshair point.
397 *
398 * @since 1.0.11
399 */
400 private Comparable domainCrosshairColumnKey;
401
402 /**
403 * The stroke used to draw the domain crosshair if it is visible.
404 *
405 * @since 1.0.11
406 */
407 private transient Stroke domainCrosshairStroke;
408
409 /**
410 * The paint used to draw the domain crosshair if it is visible.
411 *
412 * @since 1.0.11
413 */
414 private transient Paint domainCrosshairPaint;
415
416 /** A flag that controls whether or not a range crosshair is drawn. */
417 private boolean rangeCrosshairVisible;
418
419 /** The range crosshair value. */
420 private double rangeCrosshairValue;
421
422 /** The pen/brush used to draw the crosshair (if any). */
423 private transient Stroke rangeCrosshairStroke;
424
425 /** The color used to draw the crosshair (if any). */
426 private transient Paint rangeCrosshairPaint;
427
428 /**
429 * A flag that controls whether or not the crosshair locks onto actual
430 * data points.
431 */
432 private boolean rangeCrosshairLockedOnData = true;
433
434 /** A map containing lists of markers for the domain axes. */
435 private Map foregroundDomainMarkers;
436
437 /** A map containing lists of markers for the domain axes. */
438 private Map backgroundDomainMarkers;
439
440 /** A map containing lists of markers for the range axes. */
441 private Map foregroundRangeMarkers;
442
443 /** A map containing lists of markers for the range axes. */
444 private Map backgroundRangeMarkers;
445
446 /**
447 * A (possibly empty) list of annotations for the plot. The list should
448 * be initialised in the constructor and never allowed to be
449 * <code>null</code>.
450 */
451 private List annotations;
452
453 /**
454 * The weight for the plot (only relevant when the plot is used as a subplot
455 * within a combined plot).
456 */
457 private int weight;
458
459 /** The fixed space for the domain axis. */
460 private AxisSpace fixedDomainAxisSpace;
461
462 /** The fixed space for the range axis. */
463 private AxisSpace fixedRangeAxisSpace;
464
465 /**
466 * An optional collection of legend items that can be returned by the
467 * getLegendItems() method.
468 */
469 private LegendItemCollection fixedLegendItems;
470
471 /**
472 * Default constructor.
473 */
474 public CategoryPlot() {
475 this(null, null, null, null);
476 }
477
478 /**
479 * Creates a new plot.
480 *
481 * @param dataset the dataset (<code>null</code> permitted).
482 * @param domainAxis the domain axis (<code>null</code> permitted).
483 * @param rangeAxis the range axis (<code>null</code> permitted).
484 * @param renderer the item renderer (<code>null</code> permitted).
485 *
486 */
487 public CategoryPlot(CategoryDataset dataset,
488 CategoryAxis domainAxis,
489 ValueAxis rangeAxis,
490 CategoryItemRenderer renderer) {
491
492 super();
493
494 this.orientation = PlotOrientation.VERTICAL;
495
496 // allocate storage for dataset, axes and renderers
497 this.domainAxes = new ObjectList();
498 this.domainAxisLocations = new ObjectList();
499 this.rangeAxes = new ObjectList();
500 this.rangeAxisLocations = new ObjectList();
501
502 this.datasetToDomainAxisMap = new ObjectList();
503 this.datasetToRangeAxisMap = new ObjectList();
504
505 this.renderers = new ObjectList();
506
507 this.datasets = new ObjectList();
508 this.datasets.set(0, dataset);
509 if (dataset != null) {
510 dataset.addChangeListener(this);
511 }
512
513 this.axisOffset = RectangleInsets.ZERO_INSETS;
514
515 setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
516 setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
517
518 this.renderers.set(0, renderer);
519 if (renderer != null) {
520 renderer.setPlot(this);
521 renderer.addChangeListener(this);
522 }
523
524 this.domainAxes.set(0, domainAxis);
525 this.mapDatasetToDomainAxis(0, 0);
526 if (domainAxis != null) {
527 domainAxis.setPlot(this);
528 domainAxis.addChangeListener(this);
529 }
530 this.drawSharedDomainAxis = false;
531
532 this.rangeAxes.set(0, rangeAxis);
533 this.mapDatasetToRangeAxis(0, 0);
534 if (rangeAxis != null) {
535 rangeAxis.setPlot(this);
536 rangeAxis.addChangeListener(this);
537 }
538
539 configureDomainAxes();
540 configureRangeAxes();
541
542 this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
543 this.domainGridlinePosition = CategoryAnchor.MIDDLE;
544 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
545 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
546
547 this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
548 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
549 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
550
551 this.foregroundDomainMarkers = new HashMap();
552 this.backgroundDomainMarkers = new HashMap();
553 this.foregroundRangeMarkers = new HashMap();
554 this.backgroundRangeMarkers = new HashMap();
555
556 Marker baseline = new ValueMarker(0.0, new Color(0.8f, 0.8f, 0.8f,
557 0.5f), new BasicStroke(1.0f), new Color(0.85f, 0.85f, 0.95f,
558 0.5f), new BasicStroke(1.0f), 0.6f);
559 addRangeMarker(baseline, Layer.BACKGROUND);
560
561 this.anchorValue = 0.0;
562
563 this.domainCrosshairVisible = false;
564 this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
565 this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
566
567 this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE;
568 this.rangeCrosshairValue = 0.0;
569 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
570 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
571
572 this.annotations = new java.util.ArrayList();
573
574 }
575
576 /**
577 * Returns a string describing the type of plot.
578 *
579 * @return The type.
580 */
581 public String getPlotType() {
582 return localizationResources.getString("Category_Plot");
583 }
584
585 /**
586 * Returns the orientation of the plot.
587 *
588 * @return The orientation of the plot (never <code>null</code>).
589 *
590 * @see #setOrientation(PlotOrientation)
591 */
592 public PlotOrientation getOrientation() {
593 return this.orientation;
594 }
595
596 /**
597 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
598 * all registered listeners.
599 *
600 * @param orientation the orientation (<code>null</code> not permitted).
601 *
602 * @see #getOrientation()
603 */
604 public void setOrientation(PlotOrientation orientation) {
605 if (orientation == null) {
606 throw new IllegalArgumentException("Null 'orientation' argument.");
607 }
608 this.orientation = orientation;
609 fireChangeEvent();
610 }
611
612 /**
613 * Returns the axis offset.
614 *
615 * @return The axis offset (never <code>null</code>).
616 *
617 * @see #setAxisOffset(RectangleInsets)
618 */
619 public RectangleInsets getAxisOffset() {
620 return this.axisOffset;
621 }
622
623 /**
624 * Sets the axis offsets (gap between the data area and the axes) and
625 * sends a {@link PlotChangeEvent} to all registered listeners.
626 *
627 * @param offset the offset (<code>null</code> not permitted).
628 *
629 * @see #getAxisOffset()
630 */
631 public void setAxisOffset(RectangleInsets offset) {
632 if (offset == null) {
633 throw new IllegalArgumentException("Null 'offset' argument.");
634 }
635 this.axisOffset = offset;
636 fireChangeEvent();
637 }
638
639 /**
640 * Returns the domain axis for the plot. If the domain axis for this plot
641 * is <code>null</code>, then the method will return the parent plot's
642 * domain axis (if there is a parent plot).
643 *
644 * @return The domain axis (<code>null</code> permitted).
645 *
646 * @see #setDomainAxis(CategoryAxis)
647 */
648 public CategoryAxis getDomainAxis() {
649 return getDomainAxis(0);
650 }
651
652 /**
653 * Returns a domain axis.
654 *
655 * @param index the axis index.
656 *
657 * @return The axis (<code>null</code> possible).
658 *
659 * @see #setDomainAxis(int, CategoryAxis)
660 */
661 public CategoryAxis getDomainAxis(int index) {
662 CategoryAxis result = null;
663 if (index < this.domainAxes.size()) {
664 result = (CategoryAxis) this.domainAxes.get(index);
665 }
666 if (result == null) {
667 Plot parent = getParent();
668 if (parent instanceof CategoryPlot) {
669 CategoryPlot cp = (CategoryPlot) parent;
670 result = cp.getDomainAxis(index);
671 }
672 }
673 return result;
674 }
675
676 /**
677 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
678 * all registered listeners.
679 *
680 * @param axis the axis (<code>null</code> permitted).
681 *
682 * @see #getDomainAxis()
683 */
684 public void setDomainAxis(CategoryAxis axis) {
685 setDomainAxis(0, axis);
686 }
687
688 /**
689 * Sets a domain axis and sends a {@link PlotChangeEvent} to all
690 * registered listeners.
691 *
692 * @param index the axis index.
693 * @param axis the axis (<code>null</code> permitted).
694 *
695 * @see #getDomainAxis(int)
696 */
697 public void setDomainAxis(int index, CategoryAxis axis) {
698 setDomainAxis(index, axis, true);
699 }
700
701 /**
702 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
703 * all registered listeners.
704 *
705 * @param index the axis index.
706 * @param axis the axis (<code>null</code> permitted).
707 * @param notify notify listeners?
708 */
709 public void setDomainAxis(int index, CategoryAxis axis, boolean notify) {
710 CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
711 if (existing != null) {
712 existing.removeChangeListener(this);
713 }
714 if (axis != null) {
715 axis.setPlot(this);
716 }
717 this.domainAxes.set(index, axis);
718 if (axis != null) {
719 axis.configure();
720 axis.addChangeListener(this);
721 }
722 if (notify) {
723 fireChangeEvent();
724 }
725 }
726
727 /**
728 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
729 * to all registered listeners.
730 *
731 * @param axes the axes (<code>null</code> not permitted).
732 *
733 * @see #setRangeAxes(ValueAxis[])
734 */
735 public void setDomainAxes(CategoryAxis[] axes) {
736 for (int i = 0; i < axes.length; i++) {
737 setDomainAxis(i, axes[i], false);
738 }
739 fireChangeEvent();
740 }
741
742 /**
743 * Returns the index of the specified axis, or <code>-1</code> if the axis
744 * is not assigned to the plot.
745 *
746 * @param axis the axis (<code>null</code> not permitted).
747 *
748 * @return The axis index.
749 *
750 * @see #getDomainAxis(int)
751 * @see #getRangeAxisIndex(ValueAxis)
752 *
753 * @since 1.0.3
754 */
755 public int getDomainAxisIndex(CategoryAxis axis) {
756 if (axis == null) {
757 throw new IllegalArgumentException("Null 'axis' argument.");
758 }
759 return this.domainAxes.indexOf(axis);
760 }
761
762 /**
763 * Returns the domain axis location for the primary domain axis.
764 *
765 * @return The location (never <code>null</code>).
766 *
767 * @see #getRangeAxisLocation()
768 */
769 public AxisLocation getDomainAxisLocation() {
770 return getDomainAxisLocation(0);
771 }
772
773 /**
774 * Returns the location for a domain axis.
775 *
776 * @param index the axis index.
777 *
778 * @return The location.
779 *
780 * @see #setDomainAxisLocation(int, AxisLocation)
781 */
782 public AxisLocation getDomainAxisLocation(int index) {
783 AxisLocation result = null;
784 if (index < this.domainAxisLocations.size()) {
785 result = (AxisLocation) this.domainAxisLocations.get(index);
786 }
787 if (result == null) {
788 result = AxisLocation.getOpposite(getDomainAxisLocation(0));
789 }
790 return result;
791 }
792
793 /**
794 * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
795 * to all registered listeners.
796 *
797 * @param location the axis location (<code>null</code> not permitted).
798 *
799 * @see #getDomainAxisLocation()
800 * @see #setDomainAxisLocation(int, AxisLocation)
801 */
802 public void setDomainAxisLocation(AxisLocation location) {
803 // delegate...
804 setDomainAxisLocation(0, location, true);
805 }
806
807 /**
808 * Sets the location of the domain axis and, if requested, sends a
809 * {@link PlotChangeEvent} to all registered listeners.
810 *
811 * @param location the axis location (<code>null</code> not permitted).
812 * @param notify a flag that controls whether listeners are notified.
813 */
814 public void setDomainAxisLocation(AxisLocation location, boolean notify) {
815 // delegate...
816 setDomainAxisLocation(0, location, notify);
817 }
818
819 /**
820 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
821 * to all registered listeners.
822 *
823 * @param index the axis index.
824 * @param location the location.
825 *
826 * @see #getDomainAxisLocation(int)
827 * @see #setRangeAxisLocation(int, AxisLocation)
828 */
829 public void setDomainAxisLocation(int index, AxisLocation location) {
830 // delegate...
831 setDomainAxisLocation(index, location, true);
832 }
833
834 /**
835 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
836 * to all registered listeners.
837 *
838 * @param index the axis index.
839 * @param location the location.
840 * @param notify notify listeners?
841 *
842 * @since 1.0.5
843 *
844 * @see #getDomainAxisLocation(int)
845 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
846 */
847 public void setDomainAxisLocation(int index, AxisLocation location,
848 boolean notify) {
849 if (index == 0 && location == null) {
850 throw new IllegalArgumentException(
851 "Null 'location' for index 0 not permitted.");
852 }
853 this.domainAxisLocations.set(index, location);
854 if (notify) {
855 fireChangeEvent();
856 }
857 }
858
859 /**
860 * Returns the domain axis edge. This is derived from the axis location
861 * and the plot orientation.
862 *
863 * @return The edge (never <code>null</code>).
864 */
865 public RectangleEdge getDomainAxisEdge() {
866 return getDomainAxisEdge(0);
867 }
868
869 /**
870 * Returns the edge for a domain axis.
871 *
872 * @param index the axis index.
873 *
874 * @return The edge (never <code>null</code>).
875 */
876 public RectangleEdge getDomainAxisEdge(int index) {
877 RectangleEdge result = null;
878 AxisLocation location = getDomainAxisLocation(index);
879 if (location != null) {
880 result = Plot.resolveDomainAxisLocation(location, this.orientation);
881 }
882 else {
883 result = RectangleEdge.opposite(getDomainAxisEdge(0));
884 }
885 return result;
886 }
887
888 /**
889 * Returns the number of domain axes.
890 *
891 * @return The axis count.
892 */
893 public int getDomainAxisCount() {
894 return this.domainAxes.size();
895 }
896
897 /**
898 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
899 * to all registered listeners.
900 */
901 public void clearDomainAxes() {
902 for (int i = 0; i < this.domainAxes.size(); i++) {
903 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
904 if (axis != null) {
905 axis.removeChangeListener(this);
906 }
907 }
908 this.domainAxes.clear();
909 fireChangeEvent();
910 }
911
912 /**
913 * Configures the domain axes.
914 */
915 public void configureDomainAxes() {
916 for (int i = 0; i < this.domainAxes.size(); i++) {
917 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
918 if (axis != null) {
919 axis.configure();
920 }
921 }
922 }
923
924 /**
925 * Returns the range axis for the plot. If the range axis for this plot is
926 * null, then the method will return the parent plot's range axis (if there
927 * is a parent plot).
928 *
929 * @return The range axis (possibly <code>null</code>).
930 */
931 public ValueAxis getRangeAxis() {
932 return getRangeAxis(0);
933 }
934
935 /**
936 * Returns a range axis.
937 *
938 * @param index the axis index.
939 *
940 * @return The axis (<code>null</code> possible).
941 */
942 public ValueAxis getRangeAxis(int index) {
943 ValueAxis result = null;
944 if (index < this.rangeAxes.size()) {
945 result = (ValueAxis) this.rangeAxes.get(index);
946 }
947 if (result == null) {
948 Plot parent = getParent();
949 if (parent instanceof CategoryPlot) {
950 CategoryPlot cp = (CategoryPlot) parent;
951 result = cp.getRangeAxis(index);
952 }
953 }
954 return result;
955 }
956
957 /**
958 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
959 * all registered listeners.
960 *
961 * @param axis the axis (<code>null</code> permitted).
962 */
963 public void setRangeAxis(ValueAxis axis) {
964 setRangeAxis(0, axis);
965 }
966
967 /**
968 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
969 * listeners.
970 *
971 * @param index the axis index.
972 * @param axis the axis.
973 */
974 public void setRangeAxis(int index, ValueAxis axis) {
975 setRangeAxis(index, axis, true);
976 }
977
978 /**
979 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
980 * all registered listeners.
981 *
982 * @param index the axis index.
983 * @param axis the axis.
984 * @param notify notify listeners?
985 */
986 public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
987 ValueAxis existing = (ValueAxis) this.rangeAxes.get(index);
988 if (existing != null) {
989 existing.removeChangeListener(this);
990 }
991 if (axis != null) {
992 axis.setPlot(this);
993 }
994 this.rangeAxes.set(index, axis);
995 if (axis != null) {
996 axis.configure();
997 axis.addChangeListener(this);
998 }
999 if (notify) {
1000 fireChangeEvent();
1001 }
1002 }
1003
1004 /**
1005 * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1006 * to all registered listeners.
1007 *
1008 * @param axes the axes (<code>null</code> not permitted).
1009 *
1010 * @see #setDomainAxes(CategoryAxis[])
1011 */
1012 public void setRangeAxes(ValueAxis[] axes) {
1013 for (int i = 0; i < axes.length; i++) {
1014 setRangeAxis(i, axes[i], false);
1015 }
1016 fireChangeEvent();
1017 }
1018
1019 /**
1020 * Returns the index of the specified axis, or <code>-1</code> if the axis
1021 * is not assigned to the plot.
1022 *
1023 * @param axis the axis (<code>null</code> not permitted).
1024 *
1025 * @return The axis index.
1026 *
1027 * @see #getRangeAxis(int)
1028 * @see #getDomainAxisIndex(CategoryAxis)
1029 *
1030 * @since 1.0.7
1031 */
1032 public int getRangeAxisIndex(ValueAxis axis) {
1033 if (axis == null) {
1034 throw new IllegalArgumentException("Null 'axis' argument.");
1035 }
1036 int result = this.rangeAxes.indexOf(axis);
1037 if (result < 0) { // try the parent plot
1038 Plot parent = getParent();
1039 if (parent instanceof CategoryPlot) {
1040 CategoryPlot p = (CategoryPlot) parent;
1041 result = p.getRangeAxisIndex(axis);
1042 }
1043 }
1044 return result;
1045 }
1046
1047 /**
1048 * Returns the range axis location.
1049 *
1050 * @return The location (never <code>null</code>).
1051 */
1052 public AxisLocation getRangeAxisLocation() {
1053 return getRangeAxisLocation(0);
1054 }
1055
1056 /**
1057 * Returns the location for a range axis.
1058 *
1059 * @param index the axis index.
1060 *
1061 * @return The location.
1062 *
1063 * @see #setRangeAxisLocation(int, AxisLocation)
1064 */
1065 public AxisLocation getRangeAxisLocation(int index) {
1066 AxisLocation result = null;
1067 if (index < this.rangeAxisLocations.size()) {
1068 result = (AxisLocation) this.rangeAxisLocations.get(index);
1069 }
1070 if (result == null) {
1071 result = AxisLocation.getOpposite(getRangeAxisLocation(0));
1072 }
1073 return result;
1074 }
1075
1076 /**
1077 * Sets the location of the range axis and sends a {@link PlotChangeEvent}
1078 * to all registered listeners.
1079 *
1080 * @param location the location (<code>null</code> not permitted).
1081 *
1082 * @see #setRangeAxisLocation(AxisLocation, boolean)
1083 * @see #setDomainAxisLocation(AxisLocation)
1084 */
1085 public void setRangeAxisLocation(AxisLocation location) {
1086 // defer argument checking...
1087 setRangeAxisLocation(location, true);
1088 }
1089
1090 /**
1091 * Sets the location of the range axis and, if requested, sends a
1092 * {@link PlotChangeEvent} to all registered listeners.
1093 *
1094 * @param location the location (<code>null</code> not permitted).
1095 * @param notify notify listeners?
1096 *
1097 * @see #setDomainAxisLocation(AxisLocation, boolean)
1098 */
1099 public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1100 setRangeAxisLocation(0, location, notify);
1101 }
1102
1103 /**
1104 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1105 * to all registered listeners.
1106 *
1107 * @param index the axis index.
1108 * @param location the location.
1109 *
1110 * @see #getRangeAxisLocation(int)
1111 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
1112 */
1113 public void setRangeAxisLocation(int index, AxisLocation location) {
1114 setRangeAxisLocation(index, location, true);
1115 }
1116
1117 /**
1118 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1119 * to all registered listeners.
1120 *
1121 * @param index the axis index.
1122 * @param location the location.
1123 * @param notify notify listeners?
1124 *
1125 * @see #getRangeAxisLocation(int)
1126 * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1127 */
1128 public void setRangeAxisLocation(int index, AxisLocation location,
1129 boolean notify) {
1130 if (index == 0 && location == null) {
1131 throw new IllegalArgumentException(
1132 "Null 'location' for index 0 not permitted.");
1133 }
1134 this.rangeAxisLocations.set(index, location);
1135 if (notify) {
1136 fireChangeEvent();
1137 }
1138 }
1139
1140 /**
1141 * Returns the edge where the primary range axis is located.
1142 *
1143 * @return The edge (never <code>null</code>).
1144 */
1145 public RectangleEdge getRangeAxisEdge() {
1146 return getRangeAxisEdge(0);
1147 }
1148
1149 /**
1150 * Returns the edge for a range axis.
1151 *
1152 * @param index the axis index.
1153 *
1154 * @return The edge.
1155 */
1156 public RectangleEdge getRangeAxisEdge(int index) {
1157 AxisLocation location = getRangeAxisLocation(index);
1158 RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1159 this.orientation);
1160 if (result == null) {
1161 result = RectangleEdge.opposite(getRangeAxisEdge(0));
1162 }
1163 return result;
1164 }
1165
1166 /**
1167 * Returns the number of range axes.
1168 *
1169 * @return The axis count.
1170 */
1171 public int getRangeAxisCount() {
1172 return this.rangeAxes.size();
1173 }
1174
1175 /**
1176 * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1177 * to all registered listeners.
1178 */
1179 public void clearRangeAxes() {
1180 for (int i = 0; i < this.rangeAxes.size(); i++) {
1181 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1182 if (axis != null) {
1183 axis.removeChangeListener(this);
1184 }
1185 }
1186 this.rangeAxes.clear();
1187 fireChangeEvent();
1188 }
1189
1190 /**
1191 * Configures the range axes.
1192 */
1193 public void configureRangeAxes() {
1194 for (int i = 0; i < this.rangeAxes.size(); i++) {
1195 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1196 if (axis != null) {
1197 axis.configure();
1198 }
1199 }
1200 }
1201
1202 /**
1203 * Returns the primary dataset for the plot.
1204 *
1205 * @return The primary dataset (possibly <code>null</code>).
1206 *
1207 * @see #setDataset(CategoryDataset)
1208 */
1209 public CategoryDataset getDataset() {
1210 return getDataset(0);
1211 }
1212
1213 /**
1214 * Returns the dataset at the given index.
1215 *
1216 * @param index the dataset index.
1217 *
1218 * @return The dataset (possibly <code>null</code>).
1219 *
1220 * @see #setDataset(int, CategoryDataset)
1221 */
1222 public CategoryDataset getDataset(int index) {
1223 CategoryDataset result = null;
1224 if (this.datasets.size() > index) {
1225 result = (CategoryDataset) this.datasets.get(index);
1226 }
1227 return result;
1228 }
1229
1230 /**
1231 * Sets the dataset for the plot, replacing the existing dataset, if there
1232 * is one. This method also calls the
1233 * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
1234 * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
1235 * registered listeners.
1236 *
1237 * @param dataset the dataset (<code>null</code> permitted).
1238 *
1239 * @see #getDataset()
1240 */
1241 public void setDataset(CategoryDataset dataset) {
1242 setDataset(0, dataset);
1243 }
1244
1245 /**
1246 * Sets a dataset for the plot.
1247 *
1248 * @param index the dataset index.
1249 * @param dataset the dataset (<code>null</code> permitted).
1250 *
1251 * @see #getDataset(int)
1252 */
1253 public void setDataset(int index, CategoryDataset dataset) {
1254
1255 CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
1256 if (existing != null) {
1257 existing.removeChangeListener(this);
1258 }
1259 this.datasets.set(index, dataset);
1260 if (dataset != null) {
1261 dataset.addChangeListener(this);
1262 }
1263
1264 // send a dataset change event to self...
1265 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1266 datasetChanged(event);
1267
1268 }
1269
1270 /**
1271 * Returns the number of datasets.
1272 *
1273 * @return The number of datasets.
1274 *
1275 * @since 1.0.2
1276 */
1277 public int getDatasetCount() {
1278 return this.datasets.size();
1279 }
1280
1281 /**
1282 * Returns the index of the specified dataset, or <code>-1</code> if the
1283 * dataset does not belong to the plot.
1284 *
1285 * @param dataset the dataset (<code>null</code> not permitted).
1286 *
1287 * @return The index.
1288 *
1289 * @since 1.0.11
1290 */
1291 public int indexOf(CategoryDataset dataset) {
1292 int result = -1;
1293 for (int i = 0; i < this.datasets.size(); i++) {
1294 if (dataset == this.datasets.get(i)) {
1295 result = i;
1296 break;
1297 }
1298 }
1299 return result;
1300 }
1301
1302 /**
1303 * Maps a dataset to a particular domain axis.
1304 *
1305 * @param index the dataset index (zero-based).
1306 * @param axisIndex the axis index (zero-based).
1307 *
1308 * @see #getDomainAxisForDataset(int)
1309 */
1310 public void mapDatasetToDomainAxis(int index, int axisIndex) {
1311 this.datasetToDomainAxisMap.set(index, new Integer(axisIndex));
1312 // fake a dataset change event to update axes...
1313 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1314 }
1315
1316 /**
1317 * Returns the domain axis for a dataset. You can change the axis for a
1318 * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
1319 *
1320 * @param index the dataset index.
1321 *
1322 * @return The domain axis.
1323 *
1324 * @see #mapDatasetToDomainAxis(int, int)
1325 */
1326 public CategoryAxis getDomainAxisForDataset(int index) {
1327 CategoryAxis result = getDomainAxis();
1328 Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(index);
1329 if (axisIndex != null) {
1330 result = getDomainAxis(axisIndex.intValue());
1331 }
1332 return result;
1333 }
1334
1335 /**
1336 * Maps a dataset to a particular range axis.
1337 *
1338 * @param index the dataset index (zero-based).
1339 * @param axisIndex the axis index (zero-based).
1340 *
1341 * @see #getRangeAxisForDataset(int)
1342 */
1343 public void mapDatasetToRangeAxis(int index, int axisIndex) {
1344 this.datasetToRangeAxisMap.set(index, new Integer(axisIndex));
1345 // fake a dataset change event to update axes...
1346 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1347 }
1348
1349 /**
1350 * Returns the range axis for a dataset. You can change the axis for a
1351 * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
1352 *
1353 * @param index the dataset index.
1354 *
1355 * @return The range axis.
1356 *
1357 * @see #mapDatasetToRangeAxis(int, int)
1358 */
1359 public ValueAxis getRangeAxisForDataset(int index) {
1360 ValueAxis result = getRangeAxis();
1361 Integer axisIndex = (Integer) this.datasetToRangeAxisMap.get(index);
1362 if (axisIndex != null) {
1363 result = getRangeAxis(axisIndex.intValue());
1364 }
1365 return result;
1366 }
1367
1368 /**
1369 * Returns the number of renderer slots for this plot.
1370 *
1371 * @return The number of renderer slots.
1372 *
1373 * @since 1.0.11
1374 */
1375 public int getRendererCount() {
1376 return this.renderers.size();
1377 }
1378
1379 /**
1380 * Returns a reference to the renderer for the plot.
1381 *
1382 * @return The renderer.
1383 *
1384 * @see #setRenderer(CategoryItemRenderer)
1385 */
1386 public CategoryItemRenderer getRenderer() {
1387 return getRenderer(0);
1388 }
1389
1390 /**
1391 * Returns the renderer at the given index.
1392 *
1393 * @param index the renderer index.
1394 *
1395 * @return The renderer (possibly <code>null</code>).
1396 *
1397 * @see #setRenderer(int, CategoryItemRenderer)
1398 */
1399 public CategoryItemRenderer getRenderer(int index) {
1400 CategoryItemRenderer result = null;
1401 if (this.renderers.size() > index) {
1402 result = (CategoryItemRenderer) this.renderers.get(index);
1403 }
1404 return result;
1405 }
1406
1407 /**
1408 * Sets the renderer at index 0 (sometimes referred to as the "primary"
1409 * renderer) and sends a {@link PlotChangeEvent} to all registered
1410 * listeners.
1411 *
1412 * @param renderer the renderer (<code>null</code> permitted.
1413 *
1414 * @see #getRenderer()
1415 */
1416 public void setRenderer(CategoryItemRenderer renderer) {
1417 setRenderer(0, renderer, true);
1418 }
1419
1420 /**
1421 * Sets the renderer at index 0 (sometimes referred to as the "primary"
1422 * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
1423 * registered listeners.
1424 * <p>
1425 * You can set the renderer to <code>null</code>, but this is not
1426 * recommended because:
1427 * <ul>
1428 * <li>no data will be displayed;</li>
1429 * <li>the plot background will not be painted;</li>
1430 * </ul>
1431 *
1432 * @param renderer the renderer (<code>null</code> permitted).
1433 * @param notify notify listeners?
1434 *
1435 * @see #getRenderer()
1436 */
1437 public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
1438 setRenderer(0, renderer, notify);
1439 }
1440
1441 /**
1442 * Sets the renderer at the specified index and sends a
1443 * {@link PlotChangeEvent} to all registered listeners.
1444 *
1445 * @param index the index.
1446 * @param renderer the renderer (<code>null</code> permitted).
1447 *
1448 * @see #getRenderer(int)
1449 * @see #setRenderer(int, CategoryItemRenderer, boolean)
1450 */
1451 public void setRenderer(int index, CategoryItemRenderer renderer) {
1452 setRenderer(index, renderer, true);
1453 }
1454
1455 /**
1456 * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered
1457 * listeners.
1458 *
1459 * @param index the index.
1460 * @param renderer the renderer (<code>null</code> permitted).
1461 * @param notify notify listeners?
1462 *
1463 * @see #getRenderer(int)
1464 */
1465 public void setRenderer(int index, CategoryItemRenderer renderer,
1466 boolean notify) {
1467
1468 // stop listening to the existing renderer...
1469 CategoryItemRenderer existing
1470 = (CategoryItemRenderer) this.renderers.get(index);
1471 if (existing != null) {
1472 existing.removeChangeListener(this);
1473 }
1474
1475 // register the new renderer...
1476 this.renderers.set(index, renderer);
1477 if (renderer != null) {
1478 renderer.setPlot(this);
1479 renderer.addChangeListener(this);
1480 }
1481
1482 configureDomainAxes();
1483 configureRangeAxes();
1484
1485 if (notify) {
1486 fireChangeEvent();
1487 }
1488 }
1489
1490 /**
1491 * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1492 * to all registered listeners.
1493 *
1494 * @param renderers the renderers.
1495 */
1496 public void setRenderers(CategoryItemRenderer[] renderers) {
1497 for (int i = 0; i < renderers.length; i++) {
1498 setRenderer(i, renderers[i], false);
1499 }
1500 fireChangeEvent();
1501 }
1502
1503 /**
1504 * Returns the renderer for the specified dataset. If the dataset doesn't
1505 * belong to the plot, this method will return <code>null</code>.
1506 *
1507 * @param dataset the dataset (<code>null</code> permitted).
1508 *
1509 * @return The renderer (possibly <code>null</code>).
1510 */
1511 public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
1512 CategoryItemRenderer result = null;
1513 for (int i = 0; i < this.datasets.size(); i++) {
1514 if (this.datasets.get(i) == dataset) {
1515 result = (CategoryItemRenderer) this.renderers.get(i);
1516 break;
1517 }
1518 }
1519 return result;
1520 }
1521
1522 /**
1523 * Returns the index of the specified renderer, or <code>-1</code> if the
1524 * renderer is not assigned to this plot.
1525 *
1526 * @param renderer the renderer (<code>null</code> permitted).
1527 *
1528 * @return The renderer index.
1529 */
1530 public int getIndexOf(CategoryItemRenderer renderer) {
1531 return this.renderers.indexOf(renderer);
1532 }
1533
1534 /**
1535 * Returns the dataset rendering order.
1536 *
1537 * @return The order (never <code>null</code>).
1538 *
1539 * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1540 */
1541 public DatasetRenderingOrder getDatasetRenderingOrder() {
1542 return this.renderingOrder;
1543 }
1544
1545 /**
1546 * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1547 * registered listeners. By default, the plot renders the primary dataset
1548 * last (so that the primary dataset overlays the secondary datasets). You
1549 * can reverse this if you want to.
1550 *
1551 * @param order the rendering order (<code>null</code> not permitted).
1552 *
1553 * @see #getDatasetRenderingOrder()
1554 */
1555 public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1556 if (order == null) {
1557 throw new IllegalArgumentException("Null 'order' argument.");
1558 }
1559 this.renderingOrder = order;
1560 fireChangeEvent();
1561 }
1562
1563 /**
1564 * Returns the order in which the columns are rendered. The default value
1565 * is <code>SortOrder.ASCENDING</code>.
1566 *
1567 * @return The column rendering order (never <code>null</code).
1568 *
1569 * @see #setColumnRenderingOrder(SortOrder)
1570 */
1571 public SortOrder getColumnRenderingOrder() {
1572 return this.columnRenderingOrder;
1573 }
1574
1575 /**
1576 * Sets the column order in which the items in each dataset should be
1577 * rendered and sends a {@link PlotChangeEvent} to all registered
1578 * listeners. Note that this affects the order in which items are drawn,
1579 * NOT their position in the chart.
1580 *
1581 * @param order the order (<code>null</code> not permitted).
1582 *
1583 * @see #getColumnRenderingOrder()
1584 * @see #setRowRenderingOrder(SortOrder)
1585 */
1586 public void setColumnRenderingOrder(SortOrder order) {
1587 if (order == null) {
1588 throw new IllegalArgumentException("Null 'order' argument.");
1589 }
1590 this.columnRenderingOrder = order;
1591 fireChangeEvent();
1592 }
1593
1594 /**
1595 * Returns the order in which the rows should be rendered. The default
1596 * value is <code>SortOrder.ASCENDING</code>.
1597 *
1598 * @return The order (never <code>null</code>).
1599 *
1600 * @see #setRowRenderingOrder(SortOrder)
1601 */
1602 public SortOrder getRowRenderingOrder() {
1603 return this.rowRenderingOrder;
1604 }
1605
1606 /**
1607 * Sets the row order in which the items in each dataset should be
1608 * rendered and sends a {@link PlotChangeEvent} to all registered
1609 * listeners. Note that this affects the order in which items are drawn,
1610 * NOT their position in the chart.
1611 *
1612 * @param order the order (<code>null</code> not permitted).
1613 *
1614 * @see #getRowRenderingOrder()
1615 * @see #setColumnRenderingOrder(SortOrder)
1616 */
1617 public void setRowRenderingOrder(SortOrder order) {
1618 if (order == null) {
1619 throw new IllegalArgumentException("Null 'order' argument.");
1620 }
1621 this.rowRenderingOrder = order;
1622 fireChangeEvent();
1623 }
1624
1625 /**
1626 * Returns the flag that controls whether the domain grid-lines are visible.
1627 *
1628 * @return The <code>true</code> or <code>false</code>.
1629 *
1630 * @see #setDomainGridlinesVisible(boolean)
1631 */
1632 public boolean isDomainGridlinesVisible() {
1633 return this.domainGridlinesVisible;
1634 }
1635
1636 /**
1637 * Sets the flag that controls whether or not grid-lines are drawn against
1638 * the domain axis.
1639 * <p>
1640 * If the flag value changes, a {@link PlotChangeEvent} is sent to all
1641 * registered listeners.
1642 *
1643 * @param visible the new value of the flag.
1644 *
1645 * @see #isDomainGridlinesVisible()
1646 */
1647 public void setDomainGridlinesVisible(boolean visible) {
1648 if (this.domainGridlinesVisible != visible) {
1649 this.domainGridlinesVisible = visible;
1650 fireChangeEvent();
1651 }
1652 }
1653
1654 /**
1655 * Returns the position used for the domain gridlines.
1656 *
1657 * @return The gridline position (never <code>null</code>).
1658 *
1659 * @see #setDomainGridlinePosition(CategoryAnchor)
1660 */
1661 public CategoryAnchor getDomainGridlinePosition() {
1662 return this.domainGridlinePosition;
1663 }
1664
1665 /**
1666 * Sets the position used for the domain gridlines and sends a
1667 * {@link PlotChangeEvent} to all registered listeners.
1668 *
1669 * @param position the position (<code>null</code> not permitted).
1670 *
1671 * @see #getDomainGridlinePosition()
1672 */
1673 public void setDomainGridlinePosition(CategoryAnchor position) {
1674 if (position == null) {
1675 throw new IllegalArgumentException("Null 'position' argument.");
1676 }
1677 this.domainGridlinePosition = position;
1678 fireChangeEvent();
1679 }
1680
1681 /**
1682 * Returns the stroke used to draw grid-lines against the domain axis.
1683 *
1684 * @return The stroke (never <code>null</code>).
1685 *
1686 * @see #setDomainGridlineStroke(Stroke)
1687 */
1688 public Stroke getDomainGridlineStroke() {
1689 return this.domainGridlineStroke;
1690 }
1691
1692 /**
1693 * Sets the stroke used to draw grid-lines against the domain axis and
1694 * sends a {@link PlotChangeEvent} to all registered listeners.
1695 *
1696 * @param stroke the stroke (<code>null</code> not permitted).
1697 *
1698 * @see #getDomainGridlineStroke()
1699 */
1700 public void setDomainGridlineStroke(Stroke stroke) {
1701 if (stroke == null) {
1702 throw new IllegalArgumentException("Null 'stroke' not permitted.");
1703 }
1704 this.domainGridlineStroke = stroke;
1705 fireChangeEvent();
1706 }
1707
1708 /**
1709 * Returns the paint used to draw grid-lines against the domain axis.
1710 *
1711 * @return The paint (never <code>null</code>).
1712 *
1713 * @see #setDomainGridlinePaint(Paint)
1714 */
1715 public Paint getDomainGridlinePaint() {
1716 return this.domainGridlinePaint;
1717 }
1718
1719 /**
1720 * Sets the paint used to draw the grid-lines (if any) against the domain
1721 * axis and sends a {@link PlotChangeEvent} to all registered listeners.
1722 *
1723 * @param paint the paint (<code>null</code> not permitted).
1724 *
1725 * @see #getDomainGridlinePaint()
1726 */
1727 public void setDomainGridlinePaint(Paint paint) {
1728 if (paint == null) {
1729 throw new IllegalArgumentException("Null 'paint' argument.");
1730 }
1731 this.domainGridlinePaint = paint;
1732 fireChangeEvent();
1733 }
1734
1735 /**
1736 * Returns the flag that controls whether the range grid-lines are visible.
1737 *
1738 * @return The flag.
1739 *
1740 * @see #setRangeGridlinesVisible(boolean)
1741 */
1742 public boolean isRangeGridlinesVisible() {
1743 return this.rangeGridlinesVisible;
1744 }
1745
1746 /**
1747 * Sets the flag that controls whether or not grid-lines are drawn against
1748 * the range axis. If the flag changes value, a {@link PlotChangeEvent} is
1749 * sent to all registered listeners.
1750 *
1751 * @param visible the new value of the flag.
1752 *
1753 * @see #isRangeGridlinesVisible()
1754 */
1755 public void setRangeGridlinesVisible(boolean visible) {
1756 if (this.rangeGridlinesVisible != visible) {
1757 this.rangeGridlinesVisible = visible;
1758 fireChangeEvent();
1759 }
1760 }
1761
1762 /**
1763 * Returns the stroke used to draw the grid-lines against the range axis.
1764 *
1765 * @return The stroke (never <code>null</code>).
1766 *
1767 * @see #setRangeGridlineStroke(Stroke)
1768 */
1769 public Stroke getRangeGridlineStroke() {
1770 return this.rangeGridlineStroke;
1771 }
1772
1773 /**
1774 * Sets the stroke used to draw the grid-lines against the range axis and
1775 * sends a {@link PlotChangeEvent} to all registered listeners.
1776 *
1777 * @param stroke the stroke (<code>null</code> not permitted).
1778 *
1779 * @see #getRangeGridlineStroke()
1780 */
1781 public void setRangeGridlineStroke(Stroke stroke) {
1782 if (stroke == null) {
1783 throw new IllegalArgumentException("Null 'stroke' argument.");
1784 }
1785 this.rangeGridlineStroke = stroke;
1786 fireChangeEvent();
1787 }
1788
1789 /**
1790 * Returns the paint used to draw the grid-lines against the range axis.
1791 *
1792 * @return The paint (never <code>null</code>).
1793 *
1794 * @see #setRangeGridlinePaint(Paint)
1795 */
1796 public Paint getRangeGridlinePaint() {
1797 return this.rangeGridlinePaint;
1798 }
1799
1800 /**
1801 * Sets the paint used to draw the grid lines against the range axis and
1802 * sends a {@link PlotChangeEvent} to all registered listeners.
1803 *
1804 * @param paint the paint (<code>null</code> not permitted).
1805 *
1806 * @see #getRangeGridlinePaint()
1807 */
1808 public void setRangeGridlinePaint(Paint paint) {
1809 if (paint == null) {
1810 throw new IllegalArgumentException("Null 'paint' argument.");
1811 }
1812 this.rangeGridlinePaint = paint;
1813 fireChangeEvent();
1814 }
1815
1816 /**
1817 * Returns the fixed legend items, if any.
1818 *
1819 * @return The legend items (possibly <code>null</code>).
1820 *
1821 * @see #setFixedLegendItems(LegendItemCollection)
1822 */
1823 public LegendItemCollection getFixedLegendItems() {
1824 return this.fixedLegendItems;
1825 }
1826
1827 /**
1828 * Sets the fixed legend items for the plot. Leave this set to
1829 * <code>null</code> if you prefer the legend items to be created
1830 * automatically.
1831 *
1832 * @param items the legend items (<code>null</code> permitted).
1833 *
1834 * @see #getFixedLegendItems()
1835 */
1836 public void setFixedLegendItems(LegendItemCollection items) {
1837 this.fixedLegendItems = items;
1838 fireChangeEvent();
1839 }
1840
1841 /**
1842 * Returns the legend items for the plot. By default, this method creates
1843 * a legend item for each series in each of the datasets. You can change
1844 * this behaviour by overriding this method.
1845 *
1846 * @return The legend items.
1847 */
1848 public LegendItemCollection getLegendItems() {
1849 LegendItemCollection result = this.fixedLegendItems;
1850 if (result == null) {
1851 result = new LegendItemCollection();
1852 // get the legend items for the datasets...
1853 int count = this.datasets.size();
1854 for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
1855 CategoryDataset dataset = getDataset(datasetIndex);
1856 if (dataset != null) {
1857 CategoryItemRenderer renderer = getRenderer(datasetIndex);
1858 if (renderer != null) {
1859 int seriesCount = dataset.getRowCount();
1860 for (int i = 0; i < seriesCount; i++) {
1861 LegendItem item = renderer.getLegendItem(
1862 datasetIndex, i);
1863 if (item != null) {
1864 result.add(item);
1865 }
1866 }
1867 }
1868 }
1869 }
1870 }
1871 return result;
1872 }
1873
1874 /**
1875 * Handles a 'click' on the plot by updating the anchor value.
1876 *
1877 * @param x x-coordinate of the click (in Java2D space).
1878 * @param y y-coordinate of the click (in Java2D space).
1879 * @param info information about the plot's dimensions.
1880 *
1881 */
1882 public void handleClick(int x, int y, PlotRenderingInfo info) {
1883
1884 Rectangle2D dataArea = info.getDataArea();
1885 if (dataArea.contains(x, y)) {
1886 // set the anchor value for the range axis...
1887 double java2D = 0.0;
1888 if (this.orientation == PlotOrientation.HORIZONTAL) {
1889 java2D = x;
1890 }
1891 else if (this.orientation == PlotOrientation.VERTICAL) {
1892 java2D = y;
1893 }
1894 RectangleEdge edge = Plot.resolveRangeAxisLocation(
1895 getRangeAxisLocation(), this.orientation);
1896 double value = getRangeAxis().java2DToValue(
1897 java2D, info.getDataArea(), edge);
1898 setAnchorValue(value);
1899 setRangeCrosshairValue(value);
1900 }
1901
1902 }
1903
1904 /**
1905 * Zooms (in or out) on the plot's value axis.
1906 * <p>
1907 * If the value 0.0 is passed in as the zoom percent, the auto-range
1908 * calculation for the axis is restored (which sets the range to include
1909 * the minimum and maximum data values, thus displaying all the data).
1910 *
1911 * @param percent the zoom amount.
1912 */
1913 public void zoom(double percent) {
1914
1915 if (percent > 0.0) {
1916 double range = getRangeAxis().getRange().getLength();
1917 double scaledRange = range * percent;
1918 getRangeAxis().setRange(this.anchorValue - scaledRange / 2.0,
1919 this.anchorValue + scaledRange / 2.0);
1920 }
1921 else {
1922 getRangeAxis().setAutoRange(true);
1923 }
1924
1925 }
1926
1927 /**
1928 * Receives notification of a change to the plot's dataset.
1929 * <P>
1930 * The range axis bounds will be recalculated if necessary.
1931 *
1932 * @param event information about the event (not used here).
1933 */
1934 public void datasetChanged(DatasetChangeEvent event) {
1935
1936 int count = this.rangeAxes.size();
1937 for (int axisIndex = 0; axisIndex < count; axisIndex++) {
1938 ValueAxis yAxis = getRangeAxis(axisIndex);
1939 if (yAxis != null) {
1940 yAxis.configure();
1941 }
1942 }
1943 if (getParent() != null) {
1944 getParent().datasetChanged(event);
1945 }
1946 else {
1947 PlotChangeEvent e = new PlotChangeEvent(this);
1948 e.setType(ChartChangeEventType.DATASET_UPDATED);
1949 notifyListeners(e);
1950 }
1951
1952 }
1953
1954 /**
1955 * Receives notification of a renderer change event.
1956 *
1957 * @param event the event.
1958 */
1959 public void rendererChanged(RendererChangeEvent event) {
1960 Plot parent = getParent();
1961 if (parent != null) {
1962 if (parent instanceof RendererChangeListener) {
1963 RendererChangeListener rcl = (RendererChangeListener) parent;
1964 rcl.rendererChanged(event);
1965 }
1966 else {
1967 // this should never happen with the existing code, but throw
1968 // an exception in case future changes make it possible...
1969 throw new RuntimeException(
1970 "The renderer has changed and I don't know what to do!");
1971 }
1972 }
1973 else {
1974 configureRangeAxes();
1975 PlotChangeEvent e = new PlotChangeEvent(this);
1976 notifyListeners(e);
1977 }
1978 }
1979
1980 /**
1981 * Adds a marker for display (in the foreground) against the domain axis and
1982 * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
1983 * marker will be drawn by the renderer as a line perpendicular to the
1984 * domain axis, however this is entirely up to the renderer.
1985 *
1986 * @param marker the marker (<code>null</code> not permitted).
1987 *
1988 * @see #removeDomainMarker(Marker)
1989 */
1990 public void addDomainMarker(CategoryMarker marker) {
1991 addDomainMarker(marker, Layer.FOREGROUND);
1992 }
1993
1994 /**
1995 * Adds a marker for display against the domain axis and sends a
1996 * {@link PlotChangeEvent} to all registered listeners. Typically a marker
1997 * will be drawn by the renderer as a line perpendicular to the domain
1998 * axis, however this is entirely up to the renderer.
1999 *
2000 * @param marker the marker (<code>null</code> not permitted).
2001 * @param layer the layer (foreground or background) (<code>null</code>
2002 * not permitted).
2003 *
2004 * @see #removeDomainMarker(Marker, Layer)
2005 */
2006 public void addDomainMarker(CategoryMarker marker, Layer layer) {
2007 addDomainMarker(0, marker, layer);
2008 }
2009
2010 /**
2011 * Adds a marker for display by a particular renderer and sends a
2012 * {@link PlotChangeEvent} to all registered listeners.
2013 * <P>
2014 * Typically a marker will be drawn by the renderer as a line perpendicular
2015 * to a domain axis, however this is entirely up to the renderer.
2016 *
2017 * @param index the renderer index.
2018 * @param marker the marker (<code>null</code> not permitted).
2019 * @param layer the layer (<code>null</code> not permitted).
2020 *
2021 * @see #removeDomainMarker(int, Marker, Layer)
2022 */
2023 public void addDomainMarker(int index, CategoryMarker marker, Layer layer) {
2024 addDomainMarker(index, marker, layer, true);
2025 }
2026
2027 /**
2028 * Adds a marker for display by a particular renderer and, if requested,
2029 * sends a {@link PlotChangeEvent} to all registered listeners.
2030 * <P>
2031 * Typically a marker will be drawn by the renderer as a line perpendicular
2032 * to a domain axis, however this is entirely up to the renderer.
2033 *
2034 * @param index the renderer index.
2035 * @param marker the marker (<code>null</code> not permitted).
2036 * @param layer the layer (<code>null</code> not permitted).
2037 * @param notify notify listeners?
2038 *
2039 * @since 1.0.10
2040 *
2041 * @see #removeDomainMarker(int, Marker, Layer, boolean)
2042 */
2043 public void addDomainMarker(int index, CategoryMarker marker, Layer layer,
2044 boolean notify) {
2045 if (marker == null) {
2046 throw new IllegalArgumentException("Null 'marker' not permitted.");
2047 }
2048 if (layer == null) {
2049 throw new IllegalArgumentException("Null 'layer' not permitted.");
2050 }
2051 Collection markers;
2052 if (layer == Layer.FOREGROUND) {
2053 markers = (Collection) this.foregroundDomainMarkers.get(
2054 new Integer(index));
2055 if (markers == null) {
2056 markers = new java.util.ArrayList();
2057 this.foregroundDomainMarkers.put(new Integer(index), markers);
2058 }
2059 markers.add(marker);
2060 }
2061 else if (layer == Layer.BACKGROUND) {
2062 markers = (Collection) this.backgroundDomainMarkers.get(
2063 new Integer(index));
2064 if (markers == null) {
2065 markers = new java.util.ArrayList();
2066 this.backgroundDomainMarkers.put(new Integer(index), markers);
2067 }
2068 markers.add(marker);
2069 }
2070 marker.addChangeListener(this);
2071 if (notify) {
2072 fireChangeEvent();
2073 }
2074 }
2075
2076 /**
2077 * Clears all the domain markers for the plot and sends a
2078 * {@link PlotChangeEvent} to all registered listeners.
2079 *
2080 * @see #clearRangeMarkers()
2081 */
2082 public void clearDomainMarkers() {
2083 if (this.backgroundDomainMarkers != null) {
2084 Set keys = this.backgroundDomainMarkers.keySet();
2085 Iterator iterator = keys.iterator();
2086 while (iterator.hasNext()) {
2087 Integer key = (Integer) iterator.next();
2088 clearDomainMarkers(key.intValue());
2089 }
2090 this.backgroundDomainMarkers.clear();
2091 }
2092 if (this.foregroundDomainMarkers != null) {
2093 Set keys = this.foregroundDomainMarkers.keySet();
2094 Iterator iterator = keys.iterator();
2095 while (iterator.hasNext()) {
2096 Integer key = (Integer) iterator.next();
2097 clearDomainMarkers(key.intValue());
2098 }
2099 this.foregroundDomainMarkers.clear();
2100 }
2101 fireChangeEvent();
2102 }
2103
2104 /**
2105 * Returns the list of domain markers (read only) for the specified layer.
2106 *
2107 * @param layer the layer (foreground or background).
2108 *
2109 * @return The list of domain markers.
2110 */
2111 public Collection getDomainMarkers(Layer layer) {
2112 return getDomainMarkers(0, layer);
2113 }
2114
2115 /**
2116 * Returns a collection of domain markers for a particular renderer and
2117 * layer.
2118 *
2119 * @param index the renderer index.
2120 * @param layer the layer.
2121 *
2122 * @return A collection of markers (possibly <code>null</code>).
2123 */
2124 public Collection getDomainMarkers(int index, Layer layer) {
2125 Collection result = null;
2126 Integer key = new Integer(index);
2127 if (layer == Layer.FOREGROUND) {
2128 result = (Collection) this.foregroundDomainMarkers.get(key);
2129 }
2130 else if (layer == Layer.BACKGROUND) {
2131 result = (Collection) this.backgroundDomainMarkers.get(key);
2132 }
2133 if (result != null) {
2134 result = Collections.unmodifiableCollection(result);
2135 }
2136 return result;
2137 }
2138
2139 /**
2140 * Clears all the domain markers for the specified renderer.
2141 *
2142 * @param index the renderer index.
2143 *
2144 * @see #clearRangeMarkers(int)
2145 */
2146 public void clearDomainMarkers(int index) {
2147 Integer key = new Integer(index);
2148 if (this.backgroundDomainMarkers != null) {
2149 Collection markers
2150 = (Collection) this.backgroundDomainMarkers.get(key);
2151 if (markers != null) {
2152 Iterator iterator = markers.iterator();
2153 while (iterator.hasNext()) {
2154 Marker m = (Marker) iterator.next();
2155 m.removeChangeListener(this);
2156 }
2157 markers.clear();
2158 }
2159 }
2160 if (this.foregroundDomainMarkers != null) {
2161 Collection markers
2162 = (Collection) this.foregroundDomainMarkers.get(key);
2163 if (markers != null) {
2164 Iterator iterator = markers.iterator();
2165 while (iterator.hasNext()) {
2166 Marker m = (Marker) iterator.next();
2167 m.removeChangeListener(this);
2168 }
2169 markers.clear();
2170 }
2171 }
2172 fireChangeEvent();
2173 }
2174
2175 /**
2176 * Removes a marker for the domain axis and sends a {@link PlotChangeEvent}
2177 * to all registered listeners.
2178 *
2179 * @param marker the marker.
2180 *
2181 * @return A boolean indicating whether or not the marker was actually
2182 * removed.
2183 *
2184 * @since 1.0.7
2185 */
2186 public boolean removeDomainMarker(Marker marker) {
2187 return removeDomainMarker(marker, Layer.FOREGROUND);
2188 }
2189
2190 /**
2191 * Removes a marker for the domain axis in the specified layer and sends a
2192 * {@link PlotChangeEvent} to all registered listeners.
2193 *
2194 * @param marker the marker (<code>null</code> not permitted).
2195 * @param layer the layer (foreground or background).
2196 *
2197 * @return A boolean indicating whether or not the marker was actually
2198 * removed.
2199 *
2200 * @since 1.0.7
2201 */
2202 public boolean removeDomainMarker(Marker marker, Layer layer) {
2203 return removeDomainMarker(0, marker, layer);
2204 }
2205
2206 /**
2207 * Removes a marker for a specific dataset/renderer and sends a
2208 * {@link PlotChangeEvent} to all registered listeners.
2209 *
2210 * @param index the dataset/renderer index.
2211 * @param marker the marker.
2212 * @param layer the layer (foreground or background).
2213 *
2214 * @return A boolean indicating whether or not the marker was actually
2215 * removed.
2216 *
2217 * @since 1.0.7
2218 */
2219 public boolean removeDomainMarker(int index, Marker marker, Layer layer) {
2220 return removeDomainMarker(index, marker, layer, true);
2221 }
2222
2223 /**
2224 * Removes a marker for a specific dataset/renderer and, if requested,
2225 * sends a {@link PlotChangeEvent} to all registered listeners.
2226 *
2227 * @param index the dataset/renderer index.
2228 * @param marker the marker.
2229 * @param layer the layer (foreground or background).
2230 * @param notify notify listeners?
2231 *
2232 * @return A boolean indicating whether or not the marker was actually
2233 * removed.
2234 *
2235 * @since 1.0.10
2236 */
2237 public boolean removeDomainMarker(int index, Marker marker, Layer layer,
2238 boolean notify) {
2239 ArrayList markers;
2240 if (layer == Layer.FOREGROUND) {
2241 markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer(
2242 index));
2243 }
2244 else {
2245 markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer(
2246 index));
2247 }
2248 if (markers == null) {
2249 return false;
2250 }
2251 boolean removed = markers.remove(marker);
2252 if (removed && notify) {
2253 fireChangeEvent();
2254 }
2255 return removed;
2256 }
2257
2258 /**
2259 * Adds a marker for display (in the foreground) against the range axis and
2260 * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
2261 * marker will be drawn by the renderer as a line perpendicular to the
2262 * range axis, however this is entirely up to the renderer.
2263 *
2264 * @param marker the marker (<code>null</code> not permitted).
2265 *
2266 * @see #removeRangeMarker(Marker)
2267 */
2268 public void addRangeMarker(Marker marker) {
2269 addRangeMarker(marker, Layer.FOREGROUND);
2270 }
2271
2272 /**
2273 * Adds a marker for display against the range axis and sends a
2274 * {@link PlotChangeEvent} to all registered listeners. Typically a marker
2275 * will be drawn by the renderer as a line perpendicular to the range axis,
2276 * however this is entirely up to the renderer.
2277 *
2278 * @param marker the marker (<code>null</code> not permitted).
2279 * @param layer the layer (foreground or background) (<code>null</code>
2280 * not permitted).
2281 *
2282 * @see #removeRangeMarker(Marker, Layer)
2283 */
2284 public void addRangeMarker(Marker marker, Layer layer) {
2285 addRangeMarker(0, marker, layer);
2286 }
2287
2288 /**
2289 * Adds a marker for display by a particular renderer and sends a
2290 * {@link PlotChangeEvent} to all registered listeners.
2291 * <P>
2292 * Typically a marker will be drawn by the renderer as a line perpendicular
2293 * to a range axis, however this is entirely up to the renderer.
2294 *
2295 * @param index the renderer index.
2296 * @param marker the marker.
2297 * @param layer the layer.
2298 *
2299 * @see #removeRangeMarker(int, Marker, Layer)
2300 */
2301 public void addRangeMarker(int index, Marker marker, Layer layer) {
2302 addRangeMarker(index, marker, layer, true);
2303 }
2304
2305 /**
2306 * Adds a marker for display by a particular renderer and sends a
2307 * {@link PlotChangeEvent} to all registered listeners.
2308 * <P>
2309 * Typically a marker will be drawn by the renderer as a line perpendicular
2310 * to a range axis, however this is entirely up to the renderer.
2311 *
2312 * @param index the renderer index.
2313 * @param marker the marker.
2314 * @param layer the layer.
2315 * @param notify notify listeners?
2316 *
2317 * @since 1.0.10
2318 *
2319 * @see #removeRangeMarker(int, Marker, Layer, boolean)
2320 */
2321 public void addRangeMarker(int index, Marker marker, Layer layer,
2322 boolean notify) {
2323 Collection markers;
2324 if (layer == Layer.FOREGROUND) {
2325 markers = (Collection) this.foregroundRangeMarkers.get(
2326 new Integer(index));
2327 if (markers == null) {
2328 markers = new java.util.ArrayList();
2329 this.foregroundRangeMarkers.put(new Integer(index), markers);
2330 }
2331 markers.add(marker);
2332 }
2333 else if (layer == Layer.BACKGROUND) {
2334 markers = (Collection) this.backgroundRangeMarkers.get(
2335 new Integer(index));
2336 if (markers == null) {
2337 markers = new java.util.ArrayList();
2338 this.backgroundRangeMarkers.put(new Integer(index), markers);
2339 }
2340 markers.add(marker);
2341 }
2342 marker.addChangeListener(this);
2343 if (notify) {
2344 fireChangeEvent();
2345 }
2346 }
2347
2348 /**
2349 * Clears all the range markers for the plot and sends a
2350 * {@link PlotChangeEvent} to all registered listeners.
2351 *
2352 * @see #clearDomainMarkers()
2353 */
2354 public void clearRangeMarkers() {
2355 if (this.backgroundRangeMarkers != null) {
2356 Set keys = this.backgroundRangeMarkers.keySet();
2357 Iterator iterator = keys.iterator();
2358 while (iterator.hasNext()) {
2359 Integer key = (Integer) iterator.next();
2360 clearRangeMarkers(key.intValue());
2361 }
2362 this.backgroundRangeMarkers.clear();
2363 }
2364 if (this.foregroundRangeMarkers != null) {
2365 Set keys = this.foregroundRangeMarkers.keySet();
2366 Iterator iterator = keys.iterator();
2367 while (iterator.hasNext()) {
2368 Integer key = (Integer) iterator.next();
2369 clearRangeMarkers(key.intValue());
2370 }
2371 this.foregroundRangeMarkers.clear();
2372 }
2373 fireChangeEvent();
2374 }
2375
2376 /**
2377 * Returns the list of range markers (read only) for the specified layer.
2378 *
2379 * @param layer the layer (foreground or background).
2380 *
2381 * @return The list of range markers.
2382 *
2383 * @see #getRangeMarkers(int, Layer)
2384 */
2385 public Collection getRangeMarkers(Layer layer) {
2386 return getRangeMarkers(0, layer);
2387 }
2388
2389 /**
2390 * Returns a collection of range markers for a particular renderer and
2391 * layer.
2392 *
2393 * @param index the renderer index.
2394 * @param layer the layer.
2395 *
2396 * @return A collection of markers (possibly <code>null</code>).
2397 */
2398 public Collection getRangeMarkers(int index, Layer layer) {
2399 Collection result = null;
2400 Integer key = new Integer(index);
2401 if (layer == Layer.FOREGROUND) {
2402 result = (Collection) this.foregroundRangeMarkers.get(key);
2403 }
2404 else if (layer == Layer.BACKGROUND) {
2405 result = (Collection) this.backgroundRangeMarkers.get(key);
2406 }
2407 if (result != null) {
2408 result = Collections.unmodifiableCollection(result);
2409 }
2410 return result;
2411 }
2412
2413 /**
2414 * Clears all the range markers for the specified renderer.
2415 *
2416 * @param index the renderer index.
2417 *
2418 * @see #clearDomainMarkers(int)
2419 */
2420 public void clearRangeMarkers(int index) {
2421 Integer key = new Integer(index);
2422 if (this.backgroundRangeMarkers != null) {
2423 Collection markers
2424 = (Collection) this.backgroundRangeMarkers.get(key);
2425 if (markers != null) {
2426 Iterator iterator = markers.iterator();
2427 while (iterator.hasNext()) {
2428 Marker m = (Marker) iterator.next();
2429 m.removeChangeListener(this);
2430 }
2431 markers.clear();
2432 }
2433 }
2434 if (this.foregroundRangeMarkers != null) {
2435 Collection markers
2436 = (Collection) this.foregroundRangeMarkers.get(key);
2437 if (markers != null) {
2438 Iterator iterator = markers.iterator();
2439 while (iterator.hasNext()) {
2440 Marker m = (Marker) iterator.next();
2441 m.removeChangeListener(this);
2442 }
2443 markers.clear();
2444 }
2445 }
2446 fireChangeEvent();
2447 }
2448
2449 /**
2450 * Removes a marker for the range axis and sends a {@link PlotChangeEvent}
2451 * to all registered listeners.
2452 *
2453 * @param marker the marker.
2454 *
2455 * @return A boolean indicating whether or not the marker was actually
2456 * removed.
2457 *
2458 * @since 1.0.7
2459 *
2460 * @see #addRangeMarker(Marker)
2461 */
2462 public boolean removeRangeMarker(Marker marker) {
2463 return removeRangeMarker(marker, Layer.FOREGROUND);
2464 }
2465
2466 /**
2467 * Removes a marker for the range axis in the specified layer and sends a
2468 * {@link PlotChangeEvent} to all registered listeners.
2469 *
2470 * @param marker the marker (<code>null</code> not permitted).
2471 * @param layer the layer (foreground or background).
2472 *
2473 * @return A boolean indicating whether or not the marker was actually
2474 * removed.
2475 *
2476 * @since 1.0.7
2477 *
2478 * @see #addRangeMarker(Marker, Layer)
2479 */
2480 public boolean removeRangeMarker(Marker marker, Layer layer) {
2481 return removeRangeMarker(0, marker, layer);
2482 }
2483
2484 /**
2485 * Removes a marker for a specific dataset/renderer and sends a
2486 * {@link PlotChangeEvent} to all registered listeners.
2487 *
2488 * @param index the dataset/renderer index.
2489 * @param marker the marker.
2490 * @param layer the layer (foreground or background).
2491 *
2492 * @return A boolean indicating whether or not the marker was actually
2493 * removed.
2494 *
2495 * @since 1.0.7
2496 *
2497 * @see #addRangeMarker(int, Marker, Layer)
2498 */
2499 public boolean removeRangeMarker(int index, Marker marker, Layer layer) {
2500 return removeRangeMarker(index, marker, layer, true);
2501 }
2502
2503 /**
2504 * Removes a marker for a specific dataset/renderer and sends a
2505 * {@link PlotChangeEvent} to all registered listeners.
2506 *
2507 * @param index the dataset/renderer index.
2508 * @param marker the marker.
2509 * @param layer the layer (foreground or background).
2510 * @param notify notify listeners.
2511 *
2512 * @return A boolean indicating whether or not the marker was actually
2513 * removed.
2514 *
2515 * @since 1.0.10
2516 *
2517 * @see #addRangeMarker(int, Marker, Layer, boolean)
2518 */
2519 public boolean removeRangeMarker(int index, Marker marker, Layer layer,
2520 boolean notify) {
2521 if (marker == null) {
2522 throw new IllegalArgumentException("Null 'marker' argument.");
2523 }
2524 ArrayList markers;
2525 if (layer == Layer.FOREGROUND) {
2526 markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer(
2527 index));
2528 }
2529 else {
2530 markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer(
2531 index));
2532 }
2533 if (markers == null) {
2534 return false;
2535 }
2536 boolean removed = markers.remove(marker);
2537 if (removed && notify) {
2538 fireChangeEvent();
2539 }
2540 return removed;
2541 }
2542
2543 /**
2544 * Returns the flag that controls whether or not the domain crosshair is
2545 * displayed by the plot.
2546 *
2547 * @return A boolean.
2548 *
2549 * @since 1.0.11
2550 *
2551 * @see #setDomainCrosshairVisible(boolean)
2552 */
2553 public boolean isDomainCrosshairVisible() {
2554 return this.domainCrosshairVisible;
2555 }
2556
2557 /**
2558 * Sets the flag that controls whether or not the domain crosshair is
2559 * displayed by the plot, and sends a {@link PlotChangeEvent} to all
2560 * registered listeners.
2561 *
2562 * @param flag the new flag value.
2563 *
2564 * @since 1.0.11
2565 *
2566 * @see #isDomainCrosshairVisible()
2567 * @see #setRangeCrosshairVisible(boolean)
2568 */
2569 public void setDomainCrosshairVisible(boolean flag) {
2570 if (this.domainCrosshairVisible != flag) {
2571 this.domainCrosshairVisible = flag;
2572 fireChangeEvent();
2573 }
2574 }
2575
2576 /**
2577 * Returns the row key for the domain crosshair.
2578 *
2579 * @return The row key.
2580 *
2581 * @since 1.0.11
2582 */
2583 public Comparable getDomainCrosshairRowKey() {
2584 return this.domainCrosshairRowKey;
2585 }
2586
2587 /**
2588 * Sets the row key for the domain crosshair and sends a
2589 * {PlotChangeEvent} to all registered listeners.
2590 *
2591 * @param key the key.
2592 *
2593 * @since 1.0.11
2594 */
2595 public void setDomainCrosshairRowKey(Comparable key) {
2596 setDomainCrosshairRowKey(key, true);
2597 }
2598
2599 /**
2600 * Sets the row key for the domain crosshair and, if requested, sends a
2601 * {PlotChangeEvent} to all registered listeners.
2602 *
2603 * @param key the key.
2604 * @param notify notify listeners?
2605 *
2606 * @since 1.0.11
2607 */
2608 public void setDomainCrosshairRowKey(Comparable key, boolean notify) {
2609 this.domainCrosshairRowKey = key;
2610 if (notify) {
2611 fireChangeEvent();
2612 }
2613 }
2614
2615 /**
2616 * Returns the column key for the domain crosshair.
2617 *
2618 * @return The column key.
2619 *
2620 * @since 1.0.11
2621 */
2622 public Comparable getDomainCrosshairColumnKey() {
2623 return this.domainCrosshairColumnKey;
2624 }
2625
2626 /**
2627 * Sets the column key for the domain crosshair and sends
2628 * a {@link PlotChangeEvent} to all registered listeners.
2629 *
2630 * @param key the key.
2631 *
2632 * @since 1.0.11
2633 */
2634 public void setDomainCrosshairColumnKey(Comparable key) {
2635 setDomainCrosshairColumnKey(key, true);
2636 }
2637
2638 /**
2639 * Sets the column key for the domain crosshair and, if requested, sends
2640 * a {@link PlotChangeEvent} to all registered listeners.
2641 *
2642 * @param key the key.
2643 * @param notify notify listeners?
2644 *
2645 * @since 1.0.11
2646 */
2647 public void setDomainCrosshairColumnKey(Comparable key, boolean notify) {
2648 this.domainCrosshairColumnKey = key;
2649 if (notify) {
2650 fireChangeEvent();
2651 }
2652 }
2653
2654 /**
2655 * Returns the dataset index for the crosshair.
2656 *
2657 * @return The dataset index.
2658 *
2659 * @since 1.0.11
2660 */
2661 public int getCrosshairDatasetIndex() {
2662 return this.crosshairDatasetIndex;
2663 }
2664
2665 /**
2666 * Sets the dataset index for the crosshair and sends a
2667 * {@link PlotChangeEvent} to all registered listeners.
2668 *
2669 * @param index the index.
2670 *
2671 * @since 1.0.11
2672 */
2673 public void setCrosshairDatasetIndex(int index) {
2674 setCrosshairDatasetIndex(index, true);
2675 }
2676
2677 /**
2678 * Sets the dataset index for the crosshair and, if requested, sends a
2679 * {@link PlotChangeEvent} to all registered listeners.
2680 *
2681 * @param index the index.
2682 * @param notify notify listeners?
2683 *
2684 * @since 1.0.11
2685 */
2686 public void setCrosshairDatasetIndex(int index, boolean notify) {
2687 this.crosshairDatasetIndex = index;
2688 if (notify) {
2689 fireChangeEvent();
2690 }
2691 }
2692
2693 /**
2694 * Returns the paint used to draw the domain crosshair.
2695 *
2696 * @return The paint (never <code>null</code>).
2697 *
2698 * @since 1.0.11
2699 *
2700 * @see #setDomainCrosshairPaint(Paint)
2701 * @see #getDomainCrosshairStroke()
2702 */
2703 public Paint getDomainCrosshairPaint() {
2704 return this.domainCrosshairPaint;
2705 }
2706
2707 /**
2708 * Sets the paint used to draw the domain crosshair.
2709 *
2710 * @param paint the paint (<code>null</code> not permitted).
2711 *
2712 * @since 1.0.11
2713 *
2714 * @see #getDomainCrosshairPaint()
2715 */
2716 public void setDomainCrosshairPaint(Paint paint) {
2717 if (paint == null) {
2718 throw new IllegalArgumentException("Null 'paint' argument.");
2719 }
2720 this.domainCrosshairPaint = paint;
2721 fireChangeEvent();
2722 }
2723
2724 /**
2725 * Returns the stroke used to draw the domain crosshair.
2726 *
2727 * @return The stroke (never <code>null</code>).
2728 *
2729 * @since 1.0.11
2730 *
2731 * @see #setDomainCrosshairStroke(Stroke)
2732 * @see #getDomainCrosshairPaint()
2733 */
2734 public Stroke getDomainCrosshairStroke() {
2735 return this.domainCrosshairStroke;
2736 }
2737
2738 /**
2739 * Sets the stroke used to draw the domain crosshair, and sends a
2740 * {@link PlotChangeEvent} to all registered listeners.
2741 *
2742 * @param stroke the stroke (<code>null</code> not permitted).
2743 *
2744 * @since 1.0.11
2745 *
2746 * @see #getDomainCrosshairStroke()
2747 */
2748 public void setDomainCrosshairStroke(Stroke stroke) {
2749 if (stroke == null) {
2750 throw new IllegalArgumentException("Null 'stroke' argument.");
2751 }
2752 this.domainCrosshairStroke = stroke;
2753 }
2754
2755 /**
2756 * Returns a flag indicating whether or not the range crosshair is visible.
2757 *
2758 * @return The flag.
2759 *
2760 * @see #setRangeCrosshairVisible(boolean)
2761 */
2762 public boolean isRangeCrosshairVisible() {
2763 return this.rangeCrosshairVisible;
2764 }
2765
2766 /**
2767 * Sets the flag indicating whether or not the range crosshair is visible.
2768 *
2769 * @param flag the new value of the flag.
2770 *
2771 * @see #isRangeCrosshairVisible()
2772 */
2773 public void setRangeCrosshairVisible(boolean flag) {
2774 if (this.rangeCrosshairVisible != flag) {
2775 this.rangeCrosshairVisible = flag;
2776 fireChangeEvent();
2777 }
2778 }
2779
2780 /**
2781 * Returns a flag indicating whether or not the crosshair should "lock-on"
2782 * to actual data values.
2783 *
2784 * @return The flag.
2785 *
2786 * @see #setRangeCrosshairLockedOnData(boolean)
2787 */
2788 public boolean isRangeCrosshairLockedOnData() {
2789 return this.rangeCrosshairLockedOnData;
2790 }
2791
2792 /**
2793 * Sets the flag indicating whether or not the range crosshair should
2794 * "lock-on" to actual data values, and sends a {@link PlotChangeEvent}
2795 * to all registered listeners.
2796 *
2797 * @param flag the flag.
2798 *
2799 * @see #isRangeCrosshairLockedOnData()
2800 */
2801 public void setRangeCrosshairLockedOnData(boolean flag) {
2802 if (this.rangeCrosshairLockedOnData != flag) {
2803 this.rangeCrosshairLockedOnData = flag;
2804 fireChangeEvent();
2805 }
2806 }
2807
2808 /**
2809 * Returns the range crosshair value.
2810 *
2811 * @return The value.
2812 *
2813 * @see #setRangeCrosshairValue(double)
2814 */
2815 public double getRangeCrosshairValue() {
2816 return this.rangeCrosshairValue;
2817 }
2818
2819 /**
2820 * Sets the range crosshair value and, if the crosshair is visible, sends
2821 * a {@link PlotChangeEvent} to all registered listeners.
2822 *
2823 * @param value the new value.
2824 *
2825 * @see #getRangeCrosshairValue()
2826 */
2827 public void setRangeCrosshairValue(double value) {
2828 setRangeCrosshairValue(value, true);
2829 }
2830
2831 /**
2832 * Sets the range crosshair value and, if requested, sends a
2833 * {@link PlotChangeEvent} to all registered listeners (but only if the
2834 * crosshair is visible).
2835 *
2836 * @param value the new value.
2837 * @param notify a flag that controls whether or not listeners are
2838 * notified.
2839 *
2840 * @see #getRangeCrosshairValue()
2841 */
2842 public void setRangeCrosshairValue(double value, boolean notify) {
2843 this.rangeCrosshairValue = value;
2844 if (isRangeCrosshairVisible() && notify) {
2845 fireChangeEvent();
2846 }
2847 }
2848
2849 /**
2850 * Returns the pen-style (<code>Stroke</code>) used to draw the crosshair
2851 * (if visible).
2852 *
2853 * @return The crosshair stroke (never <code>null</code>).
2854 *
2855 * @see #setRangeCrosshairStroke(Stroke)
2856 * @see #isRangeCrosshairVisible()
2857 * @see #getRangeCrosshairPaint()
2858 */
2859 public Stroke getRangeCrosshairStroke() {
2860 return this.rangeCrosshairStroke;
2861 }
2862
2863 /**
2864 * Sets the pen-style (<code>Stroke</code>) used to draw the range
2865 * crosshair (if visible), and sends a {@link PlotChangeEvent} to all
2866 * registered listeners.
2867 *
2868 * @param stroke the new crosshair stroke (<code>null</code> not
2869 * permitted).
2870 *
2871 * @see #getRangeCrosshairStroke()
2872 */
2873 public void setRangeCrosshairStroke(Stroke stroke) {
2874 if (stroke == null) {
2875 throw new IllegalArgumentException("Null 'stroke' argument.");
2876 }
2877 this.rangeCrosshairStroke = stroke;
2878 fireChangeEvent();
2879 }
2880
2881 /**
2882 * Returns the paint used to draw the range crosshair.
2883 *
2884 * @return The paint (never <code>null</code>).
2885 *
2886 * @see #setRangeCrosshairPaint(Paint)
2887 * @see #isRangeCrosshairVisible()
2888 * @see #getRangeCrosshairStroke()
2889 */
2890 public Paint getRangeCrosshairPaint() {
2891 return this.rangeCrosshairPaint;
2892 }
2893
2894 /**
2895 * Sets the paint used to draw the range crosshair (if visible) and
2896 * sends a {@link PlotChangeEvent} to all registered listeners.
2897 *
2898 * @param paint the paint (<code>null</code> not permitted).
2899 *
2900 * @see #getRangeCrosshairPaint()
2901 */
2902 public void setRangeCrosshairPaint(Paint paint) {
2903 if (paint == null) {
2904 throw new IllegalArgumentException("Null 'paint' argument.");
2905 }
2906 this.rangeCrosshairPaint = paint;
2907 fireChangeEvent();
2908 }
2909
2910 /**
2911 * Returns the list of annotations.
2912 *
2913 * @return The list of annotations (never <code>null</code>).
2914 *
2915 * @see #addAnnotation(CategoryAnnotation)
2916 * @see #clearAnnotations()
2917 */
2918 public List getAnnotations() {
2919 return this.annotations;
2920 }
2921
2922 /**
2923 * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
2924 * registered listeners.
2925 *
2926 * @param annotation the annotation (<code>null</code> not permitted).
2927 *
2928 * @see #removeAnnotation(CategoryAnnotation)
2929 */
2930 public void addAnnotation(CategoryAnnotation annotation) {
2931 addAnnotation(annotation, true);
2932 }
2933
2934 /**
2935 * Adds an annotation to the plot and, if requested, sends a
2936 * {@link PlotChangeEvent} to all registered listeners.
2937 *
2938 * @param annotation the annotation (<code>null</code> not permitted).
2939 * @param notify notify listeners?
2940 *
2941 * @since 1.0.10
2942 */
2943 public void addAnnotation(CategoryAnnotation annotation, boolean notify) {
2944 if (annotation == null) {
2945 throw new IllegalArgumentException("Null 'annotation' argument.");
2946 }
2947 this.annotations.add(annotation);
2948 if (notify) {
2949 fireChangeEvent();
2950 }
2951 }
2952
2953 /**
2954 * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2955 * to all registered listeners.
2956 *
2957 * @param annotation the annotation (<code>null</code> not permitted).
2958 *
2959 * @return A boolean (indicates whether or not the annotation was removed).
2960 *
2961 * @see #addAnnotation(CategoryAnnotation)
2962 */
2963 public boolean removeAnnotation(CategoryAnnotation annotation) {
2964 return removeAnnotation(annotation, true);
2965 }
2966
2967 /**
2968 * Removes an annotation from the plot and, if requested, sends a
2969 * {@link PlotChangeEvent} to all registered listeners.
2970 *
2971 * @param annotation the annotation (<code>null</code> not permitted).
2972 * @param notify notify listeners?
2973 *
2974 * @return A boolean (indicates whether or not the annotation was removed).
2975 *
2976 * @since 1.0.10
2977 */
2978 public boolean removeAnnotation(CategoryAnnotation annotation,
2979 boolean notify) {
2980 if (annotation == null) {
2981 throw new IllegalArgumentException("Null 'annotation' argument.");
2982 }
2983 boolean removed = this.annotations.remove(annotation);
2984 if (removed && notify) {
2985 fireChangeEvent();
2986 }
2987 return removed;
2988 }
2989
2990 /**
2991 * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2992 * registered listeners.
2993 */
2994 public void clearAnnotations() {
2995 this.annotations.clear();
2996 fireChangeEvent();
2997 }
2998
2999 /**
3000 * Calculates the space required for the domain axis/axes.
3001 *
3002 * @param g2 the graphics device.
3003 * @param plotArea the plot area.
3004 * @param space a carrier for the result (<code>null</code> permitted).
3005 *
3006 * @return The required space.
3007 */
3008 protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
3009 Rectangle2D plotArea,
3010 AxisSpace space) {
3011
3012 if (space == null) {
3013 space = new AxisSpace();
3014 }
3015
3016 // reserve some space for the domain axis...
3017 if (this.fixedDomainAxisSpace != null) {
3018 if (this.orientation == PlotOrientation.HORIZONTAL) {
3019 space.ensureAtLeast(
3020 this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT);
3021 space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(),
3022 RectangleEdge.RIGHT);
3023 }
3024 else if (this.orientation == PlotOrientation.VERTICAL) {
3025 space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(),
3026 RectangleEdge.TOP);
3027 space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(),
3028 RectangleEdge.BOTTOM);
3029 }
3030 }
3031 else {
3032 // reserve space for the primary domain axis...
3033 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
3034 getDomainAxisLocation(), this.orientation);
3035 if (this.drawSharedDomainAxis) {
3036 space = getDomainAxis().reserveSpace(g2, this, plotArea,
3037 domainEdge, space);
3038 }
3039
3040 // reserve space for any domain axes...
3041 for (int i = 0; i < this.domainAxes.size(); i++) {
3042 Axis xAxis = (Axis) this.domainAxes.get(i);
3043 if (xAxis != null) {
3044 RectangleEdge edge = getDomainAxisEdge(i);
3045 space = xAxis.reserveSpace(g2, this, plotArea, edge, space);
3046 }
3047 }
3048 }
3049
3050 return space;
3051
3052 }
3053
3054 /**
3055 * Calculates the space required for the range axis/axes.
3056 *
3057 * @param g2 the graphics device.
3058 * @param plotArea the plot area.
3059 * @param space a carrier for the result (<code>null</code> permitted).
3060 *
3061 * @return The required space.
3062 */
3063 protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
3064 Rectangle2D plotArea,
3065 AxisSpace space) {
3066
3067 if (space == null) {
3068 space = new AxisSpace();
3069 }
3070
3071 // reserve some space for the range axis...
3072 if (this.fixedRangeAxisSpace != null) {
3073 if (this.orientation == PlotOrientation.HORIZONTAL) {
3074 space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(),
3075 RectangleEdge.TOP);
3076 space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(),
3077 RectangleEdge.BOTTOM);
3078 }
3079 else if (this.orientation == PlotOrientation.VERTICAL) {
3080 space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(),
3081 RectangleEdge.LEFT);
3082 space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(),
3083 RectangleEdge.RIGHT);
3084 }
3085 }
3086 else {
3087 // reserve space for the range axes (if any)...
3088 for (int i = 0; i < this.rangeAxes.size(); i++) {
3089 Axis yAxis = (Axis) this.rangeAxes.get(i);
3090 if (yAxis != null) {
3091 RectangleEdge edge = getRangeAxisEdge(i);
3092 space = yAxis.reserveSpace(g2, this, plotArea, edge, space);
3093 }
3094 }
3095 }
3096 return space;
3097
3098 }
3099
3100 /**
3101 * Calculates the space required for the axes.
3102 *
3103 * @param g2 the graphics device.
3104 * @param plotArea the plot area.
3105 *
3106 * @return The space required for the axes.
3107 */
3108 protected AxisSpace calculateAxisSpace(Graphics2D g2,
3109 Rectangle2D plotArea) {
3110 AxisSpace space = new AxisSpace();
3111 space = calculateRangeAxisSpace(g2, plotArea, space);
3112 space = calculateDomainAxisSpace(g2, plotArea, space);
3113 return space;
3114 }
3115
3116 /**
3117 * Draws the plot on a Java 2D graphics device (such as the screen or a
3118 * printer).
3119 * <P>
3120 * At your option, you may supply an instance of {@link PlotRenderingInfo}.
3121 * If you do, it will be populated with information about the drawing,
3122 * including various plot dimensions and tooltip info.
3123 *
3124 * @param g2 the graphics device.
3125 * @param area the area within which the plot (including axes) should
3126 * be drawn.
3127 * @param anchor the anchor point (<code>null</code> permitted).
3128 * @param parentState the state from the parent plot, if there is one.
3129 * @param state collects info as the chart is drawn (possibly
3130 * <code>null</code>).
3131 */
3132 public void draw(Graphics2D g2, Rectangle2D area,
3133 Point2D anchor,
3134 PlotState parentState,
3135 PlotRenderingInfo state) {
3136
3137 // if the plot area is too small, just return...
3138 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
3139 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
3140 if (b1 || b2) {
3141 return;
3142 }
3143
3144 // record the plot area...
3145 if (state == null) {
3146 // if the incoming state is null, no information will be passed
3147 // back to the caller - but we create a temporary state to record
3148 // the plot area, since that is used later by the axes
3149 state = new PlotRenderingInfo(null);
3150 }
3151 state.setPlotArea(area);
3152
3153 // adjust the drawing area for the plot insets (if any)...
3154 RectangleInsets insets = getInsets();
3155 insets.trim(area);
3156
3157 // calculate the data area...
3158 AxisSpace space = calculateAxisSpace(g2, area);
3159 Rectangle2D dataArea = space.shrink(area, null);
3160 this.axisOffset.trim(dataArea);
3161
3162 state.setDataArea(dataArea);
3163
3164 // if there is a renderer, it draws the background, otherwise use the
3165 // default background...
3166 if (getRenderer() != null) {
3167 getRenderer().drawBackground(g2, this, dataArea);
3168 }
3169 else {
3170 drawBackground(g2, dataArea);
3171 }
3172
3173 Map axisStateMap = drawAxes(g2, area, dataArea, state);
3174
3175 // the anchor point is typically the point where the mouse last
3176 // clicked - the crosshairs will be driven off this point...
3177 if (anchor != null && !dataArea.contains(anchor)) {
3178 anchor = ShapeUtilities.getPointInRectangle(anchor.getX(),
3179 anchor.getY(), dataArea);
3180 }
3181 CategoryCrosshairState crosshairState = new CategoryCrosshairState();
3182 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
3183 crosshairState.setAnchor(anchor);
3184
3185 // specify the anchor X and Y coordinates in Java2D space, for the
3186 // cases where these are not updated during rendering (i.e. no lock
3187 // on data)
3188 crosshairState.setAnchorX(Double.NaN);
3189 crosshairState.setAnchorY(Double.NaN);
3190 if (anchor != null) {
3191 ValueAxis rangeAxis = getRangeAxis();
3192 if (rangeAxis != null) {
3193 double y;
3194 if (getOrientation() == PlotOrientation.VERTICAL) {
3195 y = rangeAxis.java2DToValue(anchor.getY(), dataArea,
3196 getRangeAxisEdge());
3197 }
3198 else {
3199 y = rangeAxis.java2DToValue(anchor.getX(), dataArea,
3200 getRangeAxisEdge());
3201 }
3202 crosshairState.setAnchorY(y);
3203 }
3204 }
3205 crosshairState.setRowKey(getDomainCrosshairRowKey());
3206 crosshairState.setColumnKey(getDomainCrosshairColumnKey());
3207 crosshairState.setCrosshairY(getRangeCrosshairValue());
3208
3209 // don't let anyone draw outside the data area
3210 Shape savedClip = g2.getClip();
3211 g2.clip(dataArea);
3212
3213 drawDomainGridlines(g2, dataArea);
3214
3215 AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis());
3216 if (rangeAxisState == null) {
3217 if (parentState != null) {
3218 rangeAxisState = (AxisState) parentState.getSharedAxisStates()
3219 .get(getRangeAxis());
3220 }
3221 }
3222 if (rangeAxisState != null) {
3223 drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
3224 }
3225
3226 // draw the markers...
3227 for (int i = 0; i < this.renderers.size(); i++) {
3228 drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
3229 }
3230 for (int i = 0; i < this.renderers.size(); i++) {
3231 drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
3232 }
3233
3234 // now render data items...
3235 boolean foundData = false;
3236
3237 // set up the alpha-transparency...
3238 Composite originalComposite = g2.getComposite();
3239 g2.setComposite(AlphaComposite.getInstance(
3240 AlphaComposite.SRC_OVER, getForegroundAlpha()));
3241
3242 DatasetRenderingOrder order = getDatasetRenderingOrder();
3243 if (order == DatasetRenderingOrder.FORWARD) {
3244 for (int i = 0; i < this.datasets.size(); i++) {
3245 foundData = render(g2, dataArea, i, state, crosshairState)
3246 || foundData;
3247 }
3248 }
3249 else { // DatasetRenderingOrder.REVERSE
3250 for (int i = this.datasets.size() - 1; i >= 0; i--) {
3251 foundData = render(g2, dataArea, i, state, crosshairState)
3252 || foundData;
3253 }
3254 }
3255 // draw the foreground markers...
3256 for (int i = 0; i < this.renderers.size(); i++) {
3257 drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
3258 }
3259 for (int i = 0; i < this.renderers.size(); i++) {
3260 drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
3261 }
3262
3263 // draw the annotations (if any)...
3264 drawAnnotations(g2, dataArea);
3265
3266 g2.setClip(savedClip);
3267 g2.setComposite(originalComposite);
3268
3269 if (!foundData) {
3270 drawNoDataMessage(g2, dataArea);
3271 }
3272
3273 int datasetIndex = crosshairState.getDatasetIndex();
3274 setCrosshairDatasetIndex(datasetIndex, false);
3275
3276 // draw domain crosshair if required...
3277 Comparable rowKey = crosshairState.getRowKey();
3278 Comparable columnKey = crosshairState.getColumnKey();
3279 setDomainCrosshairRowKey(rowKey, false);
3280 setDomainCrosshairColumnKey(columnKey, false);
3281 if (isDomainCrosshairVisible() && columnKey != null) {
3282 Paint paint = getDomainCrosshairPaint();
3283 Stroke stroke = getDomainCrosshairStroke();
3284 drawDomainCrosshair(g2, dataArea, this.orientation,
3285 datasetIndex, rowKey, columnKey, stroke, paint);
3286 }
3287
3288 // draw range crosshair if required...
3289 ValueAxis yAxis = getRangeAxisForDataset(datasetIndex);
3290 RectangleEdge yAxisEdge = getRangeAxisEdge();
3291 if (!this.rangeCrosshairLockedOnData && anchor != null) {
3292 double yy;
3293 if (getOrientation() == PlotOrientation.VERTICAL) {
3294 yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge);
3295 }
3296 else {
3297 yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge);
3298 }
3299 crosshairState.setCrosshairY(yy);
3300 }
3301 setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
3302 if (isRangeCrosshairVisible()) {
3303 double y = getRangeCrosshairValue();
3304 Paint paint = getRangeCrosshairPaint();
3305 Stroke stroke = getRangeCrosshairStroke();
3306 drawRangeCrosshair(g2, dataArea, getOrientation(), y, yAxis, stroke, paint);
3307 }
3308
3309 // draw an outline around the plot area...
3310 if (isOutlineVisible()) {
3311 if (getRenderer() != null) {
3312 getRenderer().drawOutline(g2, this, dataArea);
3313 }
3314 else {
3315 drawOutline(g2, dataArea);
3316 }
3317 }
3318
3319 }
3320
3321 /**
3322 * Draws the plot background (the background color and/or image).
3323 * <P>
3324 * This method will be called during the chart drawing process and is
3325 * declared public so that it can be accessed by the renderers used by
3326 * certain subclasses. You shouldn't need to call this method directly.
3327 *
3328 * @param g2 the graphics device.
3329 * @param area the area within which the plot should be drawn.
3330 */
3331 public void drawBackground(Graphics2D g2, Rectangle2D area) {
3332 fillBackground(g2, area, this.orientation);
3333 drawBackgroundImage(g2, area);
3334 }
3335
3336 /**
3337 * A utility method for drawing the plot's axes.
3338 *
3339 * @param g2 the graphics device.
3340 * @param plotArea the plot area.
3341 * @param dataArea the data area.
3342 * @param plotState collects information about the plot (<code>null</code>
3343 * permitted).
3344 *
3345 * @return A map containing the axis states.
3346 */
3347 protected Map drawAxes(Graphics2D g2,
3348 Rectangle2D plotArea,
3349 Rectangle2D dataArea,
3350 PlotRenderingInfo plotState) {
3351
3352 AxisCollection axisCollection = new AxisCollection();
3353
3354 // add domain axes to lists...
3355 for (int index = 0; index < this.domainAxes.size(); index++) {
3356 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(index);
3357 if (xAxis != null) {
3358 axisCollection.add(xAxis, getDomainAxisEdge(index));
3359 }
3360 }
3361
3362 // add range axes to lists...
3363 for (int index = 0; index < this.rangeAxes.size(); index++) {
3364 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index);
3365 if (yAxis != null) {
3366 axisCollection.add(yAxis, getRangeAxisEdge(index));
3367 }
3368 }
3369
3370 Map axisStateMap = new HashMap();
3371
3372 // draw the top axes
3373 double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(
3374 dataArea.getHeight());
3375 Iterator iterator = axisCollection.getAxesAtTop().iterator();
3376 while (iterator.hasNext()) {
3377 Axis axis = (Axis) iterator.next();
3378 if (axis != null) {
3379 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea,
3380 RectangleEdge.TOP, plotState);
3381 cursor = axisState.getCursor();
3382 axisStateMap.put(axis, axisState);
3383 }
3384 }
3385
3386 // draw the bottom axes
3387 cursor = dataArea.getMaxY()
3388 + this.axisOffset.calculateBottomOutset(dataArea.getHeight());
3389 iterator = axisCollection.getAxesAtBottom().iterator();
3390 while (iterator.hasNext()) {
3391 Axis axis = (Axis) iterator.next();
3392 if (axis != null) {
3393 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea,
3394 RectangleEdge.BOTTOM, plotState);
3395 cursor = axisState.getCursor();
3396 axisStateMap.put(axis, axisState);
3397 }
3398 }
3399
3400 // draw the left axes
3401 cursor = dataArea.getMinX()
3402 - this.axisOffset.calculateLeftOutset(dataArea.getWidth());
3403 iterator = axisCollection.getAxesAtLeft().iterator();
3404 while (iterator.hasNext()) {
3405 Axis axis = (Axis) iterator.next();
3406 if (axis != null) {
3407 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea,
3408 RectangleEdge.LEFT, plotState);
3409 cursor = axisState.getCursor();
3410 axisStateMap.put(axis, axisState);
3411 }
3412 }
3413
3414 // draw the right axes
3415 cursor = dataArea.getMaxX()
3416 + this.axisOffset.calculateRightOutset(dataArea.getWidth());
3417 iterator = axisCollection.getAxesAtRight().iterator();
3418 while (iterator.hasNext()) {
3419 Axis axis = (Axis) iterator.next();
3420 if (axis != null) {
3421 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea,
3422 RectangleEdge.RIGHT, plotState);
3423 cursor = axisState.getCursor();
3424 axisStateMap.put(axis, axisState);
3425 }
3426 }
3427
3428 return axisStateMap;
3429
3430 }
3431
3432 /**
3433 * Draws a representation of a dataset within the dataArea region using the
3434 * appropriate renderer.
3435 *
3436 * @param g2 the graphics device.
3437 * @param dataArea the region in which the data is to be drawn.
3438 * @param index the dataset and renderer index.
3439 * @param info an optional object for collection dimension information.
3440 * @param crosshairState a state object for tracking crosshair info
3441 * (<code>null</code> permitted).
3442 *
3443 * @return A boolean that indicates whether or not real data was found.
3444 *
3445 * @since 1.0.11
3446 */
3447 public boolean render(Graphics2D g2, Rectangle2D dataArea, int index,
3448 PlotRenderingInfo info, CategoryCrosshairState crosshairState) {
3449
3450 boolean foundData = false;
3451 CategoryDataset currentDataset = getDataset(index);
3452 CategoryItemRenderer renderer = getRenderer(index);
3453 CategoryAxis domainAxis = getDomainAxisForDataset(index);
3454 ValueAxis rangeAxis = getRangeAxisForDataset(index);
3455 boolean hasData = !DatasetUtilities.isEmptyOrNull(currentDataset);
3456 if (hasData && renderer != null) {
3457
3458 foundData = true;
3459 CategoryItemRendererState state = renderer.initialise(g2, dataArea,
3460 this, index, info);
3461 state.setCrosshairState(crosshairState);
3462 int columnCount = currentDataset.getColumnCount();
3463 int rowCount = currentDataset.getRowCount();
3464 int passCount = renderer.getPassCount();
3465 for (int pass = 0; pass < passCount; pass++) {
3466 if (this.columnRenderingOrder == SortOrder.ASCENDING) {
3467 for (int column = 0; column < columnCount; column++) {
3468 if (this.rowRenderingOrder == SortOrder.ASCENDING) {
3469 for (int row = 0; row < rowCount; row++) {
3470 renderer.drawItem(g2, state, dataArea, this,
3471 domainAxis, rangeAxis, currentDataset,
3472 row, column, pass);
3473 }
3474 }
3475 else {
3476 for (int row = rowCount - 1; row >= 0; row--) {
3477 renderer.drawItem(g2, state, dataArea, this,
3478 domainAxis, rangeAxis, currentDataset,
3479 row, column, pass);
3480 }
3481 }
3482 }
3483 }
3484 else {
3485 for (int column = columnCount - 1; column >= 0; column--) {
3486 if (this.rowRenderingOrder == SortOrder.ASCENDING) {
3487 for (int row = 0; row < rowCount; row++) {
3488 renderer.drawItem(g2, state, dataArea, this,
3489 domainAxis, rangeAxis, currentDataset,
3490 row, column, pass);
3491 }
3492 }
3493 else {
3494 for (int row = rowCount - 1; row >= 0; row--) {
3495 renderer.drawItem(g2, state, dataArea, this,
3496 domainAxis, rangeAxis, currentDataset,
3497 row, column, pass);
3498 }
3499 }
3500 }
3501 }
3502 }
3503 }
3504 return foundData;
3505
3506 }
3507
3508 /**
3509 * Draws the gridlines for the plot.
3510 *
3511 * @param g2 the graphics device.
3512 * @param dataArea the area inside the axes.
3513 *
3514 * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List)
3515 */
3516 protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea) {
3517
3518 // draw the domain grid lines, if any...
3519 if (isDomainGridlinesVisible()) {
3520 CategoryAnchor anchor = getDomainGridlinePosition();
3521 RectangleEdge domainAxisEdge = getDomainAxisEdge();
3522 Stroke gridStroke = getDomainGridlineStroke();
3523 Paint gridPaint = getDomainGridlinePaint();
3524 if ((gridStroke != null) && (gridPaint != null)) {
3525 // iterate over the categories
3526 CategoryDataset data = getDataset();
3527 if (data != null) {
3528 CategoryAxis axis = getDomainAxis();
3529 if (axis != null) {
3530 int columnCount = data.getColumnCount();
3531 for (int c = 0; c < columnCount; c++) {
3532 double xx = axis.getCategoryJava2DCoordinate(
3533 anchor, c, columnCount, dataArea,
3534 domainAxisEdge);
3535 CategoryItemRenderer renderer1 = getRenderer();
3536 if (renderer1 != null) {
3537 renderer1.drawDomainGridline(g2, this,
3538 dataArea, xx);
3539 }
3540 }
3541 }
3542 }
3543 }
3544 }
3545 }
3546
3547 /**
3548 * Draws the gridlines for the plot.
3549 *
3550 * @param g2 the graphics device.
3551 * @param dataArea the area inside the axes.
3552 * @param ticks the ticks.
3553 *
3554 * @see #drawDomainGridlines(Graphics2D, Rectangle2D)
3555 */
3556 protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea,
3557 List ticks) {
3558 // draw the range grid lines, if any...
3559 if (isRangeGridlinesVisible()) {
3560 Stroke gridStroke = getRangeGridlineStroke();
3561 Paint gridPaint = getRangeGridlinePaint();
3562 if ((gridStroke != null) && (gridPaint != null)) {
3563 ValueAxis axis = getRangeAxis();
3564 if (axis != null) {
3565 Iterator iterator = ticks.iterator();
3566 while (iterator.hasNext()) {
3567 ValueTick tick = (ValueTick) iterator.next();
3568 CategoryItemRenderer renderer1 = getRenderer();
3569 if (renderer1 != null) {
3570 renderer1.drawRangeGridline(g2, this,
3571 getRangeAxis(), dataArea, tick.getValue());
3572 }
3573 }
3574 }
3575 }
3576 }
3577 }
3578
3579 /**
3580 * Draws the annotations.
3581 *
3582 * @param g2 the graphics device.
3583 * @param dataArea the data area.
3584 */
3585 protected void drawAnnotations(Graphics2D g2, Rectangle2D dataArea) {
3586
3587 if (getAnnotations() != null) {
3588 Iterator iterator = getAnnotations().iterator();
3589 while (iterator.hasNext()) {
3590 CategoryAnnotation annotation
3591 = (CategoryAnnotation) iterator.next();
3592 annotation.draw(g2, this, dataArea, getDomainAxis(),
3593 getRangeAxis());
3594 }
3595 }
3596
3597 }
3598
3599 /**
3600 * Draws the domain markers (if any) for an axis and layer. This method is
3601 * typically called from within the draw() method.
3602 *
3603 * @param g2 the graphics device.
3604 * @param dataArea the data area.
3605 * @param index the renderer index.
3606 * @param layer the layer (foreground or background).
3607 *
3608 * @see #drawRangeMarkers(Graphics2D, Rectangle2D, int, Layer)
3609 */
3610 protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea,
3611 int index, Layer layer) {
3612
3613 CategoryItemRenderer r = getRenderer(index);
3614 if (r == null) {
3615 return;
3616 }
3617
3618 Collection markers = getDomainMarkers(index, layer);
3619 CategoryAxis axis = getDomainAxisForDataset(index);
3620 if (markers != null && axis != null) {
3621 Iterator iterator = markers.iterator();
3622 while (iterator.hasNext()) {
3623 CategoryMarker marker = (CategoryMarker) iterator.next();
3624 r.drawDomainMarker(g2, this, axis, marker, dataArea);
3625 }
3626 }
3627
3628 }
3629
3630 /**
3631 * Draws the range markers (if any) for an axis and layer. This method is
3632 * typically called from within the draw() method.
3633 *
3634 * @param g2 the graphics device.
3635 * @param dataArea the data area.
3636 * @param index the renderer index.
3637 * @param layer the layer (foreground or background).
3638 *
3639 * @see #drawDomainMarkers(Graphics2D, Rectangle2D, int, Layer)
3640 */
3641 protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea,
3642 int index, Layer layer) {
3643
3644 CategoryItemRenderer r = getRenderer(index);
3645 if (r == null) {
3646 return;
3647 }
3648
3649 Collection markers = getRangeMarkers(index, layer);
3650 ValueAxis axis = getRangeAxisForDataset(index);
3651 if (markers != null && axis != null) {
3652 Iterator iterator = markers.iterator();
3653 while (iterator.hasNext()) {
3654 Marker marker = (Marker) iterator.next();
3655 r.drawRangeMarker(g2, this, axis, marker, dataArea);
3656 }
3657 }
3658
3659 }
3660
3661 /**
3662 * Utility method for drawing a line perpendicular to the range axis (used
3663 * for crosshairs).
3664 *
3665 * @param g2 the graphics device.
3666 * @param dataArea the area defined by the axes.
3667 * @param value the data value.
3668 * @param stroke the line stroke (<code>null</code> not permitted).
3669 * @param paint the line paint (<code>null</code> not permitted).
3670 */
3671 protected void drawRangeLine(Graphics2D g2, Rectangle2D dataArea,
3672 double value, Stroke stroke, Paint paint) {
3673
3674 double java2D = getRangeAxis().valueToJava2D(value, dataArea,
3675 getRangeAxisEdge());
3676 Line2D line = null;
3677 if (this.orientation == PlotOrientation.HORIZONTAL) {
3678 line = new Line2D.Double(java2D, dataArea.getMinY(), java2D,
3679 dataArea.getMaxY());
3680 }
3681 else if (this.orientation == PlotOrientation.VERTICAL) {
3682 line = new Line2D.Double(dataArea.getMinX(), java2D,
3683 dataArea.getMaxX(), java2D);
3684 }
3685 g2.setStroke(stroke);
3686 g2.setPaint(paint);
3687 g2.draw(line);
3688
3689 }
3690
3691 /**
3692 * Draws a domain crosshair.
3693 *
3694 * @param g2 the graphics target.
3695 * @param dataArea the data area.
3696 * @param orientation the plot orientation.
3697 * @param datasetIndex the dataset index.
3698 * @param rowKey the row key.
3699 * @param columnKey the column key.
3700 * @param stroke the stroke used to draw the crosshair line.
3701 * @param paint the paint used to draw the crosshair line.
3702 *
3703 * @see #drawRangeCrosshair(Graphics2D, Rectangle2D, PlotOrientation,
3704 * double, ValueAxis, Stroke, Paint)
3705 *
3706 * @since 1.0.11
3707 */
3708 protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea,
3709 PlotOrientation orientation, int datasetIndex,
3710 Comparable rowKey, Comparable columnKey, Stroke stroke,
3711 Paint paint) {
3712
3713 CategoryDataset dataset = getDataset(datasetIndex);
3714 CategoryAxis axis = getDomainAxisForDataset(datasetIndex);
3715 CategoryItemRenderer renderer = getRenderer(datasetIndex);
3716 Line2D line = null;
3717 if (orientation == PlotOrientation.VERTICAL) {
3718 double xx = renderer.getItemMiddle(rowKey, columnKey, dataset, axis,
3719 dataArea, RectangleEdge.BOTTOM);
3720 line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3721 dataArea.getMaxY());
3722 }
3723 else {
3724 double yy = renderer.getItemMiddle(rowKey, columnKey, dataset, axis,
3725 dataArea, RectangleEdge.LEFT);
3726 line = new Line2D.Double(dataArea.getMinX(), yy,
3727 dataArea.getMaxX(), yy);
3728 }
3729 g2.setStroke(stroke);
3730 g2.setPaint(paint);
3731 g2.draw(line);
3732
3733 }
3734
3735 /**
3736 * Draws a range crosshair.
3737 *
3738 * @param g2 the graphics target.
3739 * @param dataArea the data area.
3740 * @param orientation the plot orientation.
3741 * @param value the crosshair value.
3742 * @param axis the axis against which the value is measured.
3743 * @param stroke the stroke used to draw the crosshair line.
3744 * @param paint the paint used to draw the crosshair line.
3745 *
3746 * @see #drawDomainCrosshair(Graphics2D, Rectangle2D, PlotOrientation, int,
3747 * Comparable, Comparable, Stroke, Paint)
3748 *
3749 * @since 1.0.5
3750 */
3751 protected void drawRangeCrosshair(Graphics2D g2, Rectangle2D dataArea,
3752 PlotOrientation orientation, double value, ValueAxis axis,
3753 Stroke stroke, Paint paint) {
3754
3755 if (!axis.getRange().contains(value)) {
3756 return;
3757 }
3758 Line2D line = null;
3759 if (orientation == PlotOrientation.HORIZONTAL) {
3760 double xx = axis.valueToJava2D(value, dataArea,
3761 RectangleEdge.BOTTOM);
3762 line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3763 dataArea.getMaxY());
3764 }
3765 else {
3766 double yy = axis.valueToJava2D(value, dataArea,
3767 RectangleEdge.LEFT);
3768 line = new Line2D.Double(dataArea.getMinX(), yy,
3769 dataArea.getMaxX(), yy);
3770 }
3771 g2.setStroke(stroke);
3772 g2.setPaint(paint);
3773 g2.draw(line);
3774
3775 }
3776
3777 /**
3778 * Returns the range of data values that will be plotted against the range
3779 * axis. If the dataset is <code>null</code>, this method returns
3780 * <code>null</code>.
3781 *
3782 * @param axis the axis.
3783 *
3784 * @return The data range.
3785 */
3786 public Range getDataRange(ValueAxis axis) {
3787
3788 Range result = null;
3789 List mappedDatasets = new ArrayList();
3790
3791 int rangeIndex = this.rangeAxes.indexOf(axis);
3792 if (rangeIndex >= 0) {
3793 mappedDatasets.addAll(datasetsMappedToRangeAxis(rangeIndex));
3794 }
3795 else if (axis == getRangeAxis()) {
3796 mappedDatasets.addAll(datasetsMappedToRangeAxis(0));
3797 }
3798
3799 // iterate through the datasets that map to the axis and get the union
3800 // of the ranges.
3801 Iterator iterator = mappedDatasets.iterator();
3802 while (iterator.hasNext()) {
3803 CategoryDataset d = (CategoryDataset) iterator.next();
3804 CategoryItemRenderer r = getRendererForDataset(d);
3805 if (r != null) {
3806 result = Range.combine(result, r.findRangeBounds(d));
3807 }
3808 }
3809 return result;
3810
3811 }
3812
3813 /**
3814 * Returns a list of the datasets that are mapped to the axis with the
3815 * specified index.
3816 *
3817 * @param axisIndex the axis index.
3818 *
3819 * @return The list (possibly empty, but never <code>null</code>).
3820 *
3821 * @since 1.0.3
3822 */
3823 private List datasetsMappedToDomainAxis(int axisIndex) {
3824 List result = new ArrayList();
3825 for (int datasetIndex = 0; datasetIndex < this.datasets.size();
3826 datasetIndex++) {
3827 Object dataset = this.datasets.get(datasetIndex);
3828 if (dataset != null) {
3829 Integer m = (Integer) this.datasetToDomainAxisMap.get(
3830 datasetIndex);
3831 if (m == null) { // a dataset with no mapping is assigned to
3832 // axis 0
3833 if (axisIndex == 0) {
3834 result.add(dataset);
3835 }
3836 }
3837 else {
3838 if (m.intValue() == axisIndex) {
3839 result.add(dataset);
3840 }
3841 }
3842 }
3843 }
3844 return result;
3845 }
3846
3847 /**
3848 * A utility method that returns a list of datasets that are mapped to a
3849 * given range axis.
3850 *
3851 * @param index the axis index.
3852 *
3853 * @return A list of datasets.
3854 */
3855 private List datasetsMappedToRangeAxis(int index) {
3856 List result = new ArrayList();
3857 for (int i = 0; i < this.datasets.size(); i++) {
3858 Object dataset = this.datasets.get(i);
3859 if (dataset != null) {
3860 Integer m = (Integer) this.datasetToRangeAxisMap.get(i);
3861 if (m == null) { // a dataset with no mapping is assigned to
3862 // axis 0
3863 if (index == 0) {
3864 result.add(dataset);
3865 }
3866 }
3867 else {
3868 if (m.intValue() == index) {
3869 result.add(dataset);
3870 }
3871 }
3872 }
3873 }
3874 return result;
3875 }
3876
3877 /**
3878 * Returns the weight for this plot when it is used as a subplot within a
3879 * combined plot.
3880 *
3881 * @return The weight.
3882 *
3883 * @see #setWeight(int)
3884 */
3885 public int getWeight() {
3886 return this.weight;
3887 }
3888
3889 /**
3890 * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all
3891 * registered listeners.
3892 *
3893 * @param weight the weight.
3894 *
3895 * @see #getWeight()
3896 */
3897 public void setWeight(int weight) {
3898 this.weight = weight;
3899 fireChangeEvent();
3900 }
3901
3902 /**
3903 * Returns the fixed domain axis space.
3904 *
3905 * @return The fixed domain axis space (possibly <code>null</code>).
3906 *
3907 * @see #setFixedDomainAxisSpace(AxisSpace)
3908 */
3909 public AxisSpace getFixedDomainAxisSpace() {
3910 return this.fixedDomainAxisSpace;
3911 }
3912
3913 /**
3914 * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to
3915 * all registered listeners.
3916 *
3917 * @param space the space (<code>null</code> permitted).
3918 *
3919 * @see #getFixedDomainAxisSpace()
3920 */
3921 public void setFixedDomainAxisSpace(AxisSpace space) {
3922 setFixedDomainAxisSpace(space, true);
3923 }
3924
3925 /**
3926 * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to
3927 * all registered listeners.
3928 *
3929 * @param space the space (<code>null</code> permitted).
3930 * @param notify notify listeners?
3931 *
3932 * @see #getFixedDomainAxisSpace()
3933 *
3934 * @since 1.0.7
3935 */
3936 public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) {
3937 this.fixedDomainAxisSpace = space;
3938 if (notify) {
3939 fireChangeEvent();
3940 }
3941 }
3942
3943 /**
3944 * Returns the fixed range axis space.
3945 *
3946 * @return The fixed range axis space (possibly <code>null</code>).
3947 *
3948 * @see #setFixedRangeAxisSpace(AxisSpace)
3949 */
3950 public AxisSpace getFixedRangeAxisSpace() {
3951 return this.fixedRangeAxisSpace;
3952 }
3953
3954 /**
3955 * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to
3956 * all registered listeners.
3957 *
3958 * @param space the space (<code>null</code> permitted).
3959 *
3960 * @see #getFixedRangeAxisSpace()
3961 */
3962 public void setFixedRangeAxisSpace(AxisSpace space) {
3963 setFixedRangeAxisSpace(space, true);
3964 }
3965
3966 /**
3967 * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to
3968 * all registered listeners.
3969 *
3970 * @param space the space (<code>null</code> permitted).
3971 * @param notify notify listeners?
3972 *
3973 * @see #getFixedRangeAxisSpace()
3974 *
3975 * @since 1.0.7
3976 */
3977 public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) {
3978 this.fixedRangeAxisSpace = space;
3979 if (notify) {
3980 fireChangeEvent();
3981 }
3982 }
3983
3984 /**
3985 * Returns a list of the categories in the plot's primary dataset.
3986 *
3987 * @return A list of the categories in the plot's primary dataset.
3988 *
3989 * @see #getCategoriesForAxis(CategoryAxis)
3990 */
3991 public List getCategories() {
3992 List result = null;
3993 if (getDataset() != null) {
3994 result = Collections.unmodifiableList(getDataset().getColumnKeys());
3995 }
3996 return result;
3997 }
3998
3999 /**
4000 * Returns a list of the categories that should be displayed for the
4001 * specified axis.
4002 *
4003 * @param axis the axis (<code>null</code> not permitted)
4004 *
4005 * @return The categories.
4006 *
4007 * @since 1.0.3
4008 */
4009 public List getCategoriesForAxis(CategoryAxis axis) {
4010 List result = new ArrayList();
4011 int axisIndex = this.domainAxes.indexOf(axis);
4012 List datasets = datasetsMappedToDomainAxis(axisIndex);
4013 Iterator iterator = datasets.iterator();
4014 while (iterator.hasNext()) {
4015 CategoryDataset dataset = (CategoryDataset) iterator.next();
4016 // add the unique categories from this dataset
4017 for (int i = 0; i < dataset.getColumnCount(); i++) {
4018 Comparable category = dataset.getColumnKey(i);
4019 if (!result.contains(category)) {
4020 result.add(category);
4021 }
4022 }
4023 }
4024 return result;
4025 }
4026
4027 /**
4028 * Returns the flag that controls whether or not the shared domain axis is
4029 * drawn for each subplot.
4030 *
4031 * @return A boolean.
4032 *
4033 * @see #setDrawSharedDomainAxis(boolean)
4034 */
4035 public boolean getDrawSharedDomainAxis() {
4036 return this.drawSharedDomainAxis;
4037 }
4038
4039 /**
4040 * Sets the flag that controls whether the shared domain axis is drawn when
4041 * this plot is being used as a subplot.
4042 *
4043 * @param draw a boolean.
4044 *
4045 * @see #getDrawSharedDomainAxis()
4046 */
4047 public void setDrawSharedDomainAxis(boolean draw) {
4048 this.drawSharedDomainAxis = draw;
4049 fireChangeEvent();
4050 }
4051
4052 /**
4053 * Returns <code>false</code> to indicate that the domain axes are not
4054 * zoomable.
4055 *
4056 * @return A boolean.
4057 *
4058 * @see #isRangeZoomable()
4059 */
4060 public boolean isDomainZoomable() {
4061 return false;
4062 }
4063
4064 /**
4065 * Returns <code>true</code> to indicate that the range axes are zoomable.
4066 *
4067 * @return A boolean.
4068 *
4069 * @see #isDomainZoomable()
4070 */
4071 public boolean isRangeZoomable() {
4072 return true;
4073 }
4074
4075 /**
4076 * This method does nothing, because <code>CategoryPlot</code> doesn't
4077 * support zooming on the domain.
4078 *
4079 * @param factor the zoom factor.
4080 * @param state the plot state.
4081 * @param source the source point (in Java2D space) for the zoom.
4082 */
4083 public void zoomDomainAxes(double factor, PlotRenderingInfo state,
4084 Point2D source) {
4085 // can't zoom domain axis
4086 }
4087
4088 /**
4089 * This method does nothing, because <code>CategoryPlot</code> doesn't
4090 * support zooming on the domain.
4091 *
4092 * @param lowerPercent the lower bound.
4093 * @param upperPercent the upper bound.
4094 * @param state the plot state.
4095 * @param source the source point (in Java2D space) for the zoom.
4096 */
4097 public void zoomDomainAxes(double lowerPercent, double upperPercent,
4098 PlotRenderingInfo state, Point2D source) {
4099 // can't zoom domain axis
4100 }
4101
4102 /**
4103 * This method does nothing, because <code>CategoryPlot</code> doesn't
4104 * support zooming on the domain.
4105 *
4106 * @param factor the zoom factor.
4107 * @param info the plot rendering info.
4108 * @param source the source point (in Java2D space).
4109 * @param useAnchor use source point as zoom anchor?
4110 *
4111 * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean)
4112 *
4113 * @since 1.0.7
4114 */
4115 public void zoomDomainAxes(double factor, PlotRenderingInfo info,
4116 Point2D source, boolean useAnchor) {
4117 // can't zoom domain axis
4118 }
4119
4120 /**
4121 * Multiplies the range on the range axis/axes by the specified factor.
4122 *
4123 * @param factor the zoom factor.
4124 * @param state the plot state.
4125 * @param source the source point (in Java2D space) for the zoom.
4126 */
4127 public void zoomRangeAxes(double factor, PlotRenderingInfo state,
4128 Point2D source) {
4129 // delegate to other method
4130 zoomRangeAxes(factor, state, source, false);
4131 }
4132
4133 /**
4134 * Multiplies the range on the range axis/axes by the specified factor.
4135 *
4136 * @param factor the zoom factor.
4137 * @param info the plot rendering info.
4138 * @param source the source point.
4139 * @param useAnchor a flag that controls whether or not the source point
4140 * is used for the zoom anchor.
4141 *
4142 * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean)
4143 *
4144 * @since 1.0.7
4145 */
4146 public void zoomRangeAxes(double factor, PlotRenderingInfo info,
4147 Point2D source, boolean useAnchor) {
4148
4149 // perform the zoom on each range axis
4150 for (int i = 0; i < this.rangeAxes.size(); i++) {
4151 ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
4152 if (rangeAxis != null) {
4153 if (useAnchor) {
4154 // get the relevant source coordinate given the plot
4155 // orientation
4156 double sourceY = source.getY();
4157 if (this.orientation == PlotOrientation.HORIZONTAL) {
4158 sourceY = source.getX();
4159 }
4160 double anchorY = rangeAxis.java2DToValue(sourceY,
4161 info.getDataArea(), getRangeAxisEdge());
4162 rangeAxis.resizeRange(factor, anchorY);
4163 }
4164 else {
4165 rangeAxis.resizeRange(factor);
4166 }
4167 }
4168 }
4169 }
4170
4171 /**
4172 * Zooms in on the range axes.
4173 *
4174 * @param lowerPercent the lower bound.
4175 * @param upperPercent the upper bound.
4176 * @param state the plot state.
4177 * @param source the source point (in Java2D space) for the zoom.
4178 */
4179 public void zoomRangeAxes(double lowerPercent, double upperPercent,
4180 PlotRenderingInfo state, Point2D source) {
4181 for (int i = 0; i < this.rangeAxes.size(); i++) {
4182 ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
4183 if (rangeAxis != null) {
4184 rangeAxis.zoomRange(lowerPercent, upperPercent);
4185 }
4186 }
4187 }
4188
4189 /**
4190 * Returns the anchor value.
4191 *
4192 * @return The anchor value.
4193 *
4194 * @see #setAnchorValue(double)
4195 */
4196 public double getAnchorValue() {
4197 return this.anchorValue;
4198 }
4199
4200 /**
4201 * Sets the anchor value and sends a {@link PlotChangeEvent} to all
4202 * registered listeners.
4203 *
4204 * @param value the anchor value.
4205 *
4206 * @see #getAnchorValue()
4207 */
4208 public void setAnchorValue(double value) {
4209 setAnchorValue(value, true);
4210 }
4211
4212 /**
4213 * Sets the anchor value and, if requested, sends a {@link PlotChangeEvent}
4214 * to all registered listeners.
4215 *
4216 * @param value the value.
4217 * @param notify notify listeners?
4218 *
4219 * @see #getAnchorValue()
4220 */
4221 public void setAnchorValue(double value, boolean notify) {
4222 this.anchorValue = value;
4223 if (notify) {
4224 fireChangeEvent();
4225 }
4226 }
4227
4228 /**
4229 * Tests the plot for equality with an arbitrary object.
4230 *
4231 * @param obj the object to test against (<code>null</code> permitted).
4232 *
4233 * @return A boolean.
4234 */
4235 public boolean equals(Object obj) {
4236
4237 if (obj == this) {
4238 return true;
4239 }
4240 if (!(obj instanceof CategoryPlot)) {
4241 return false;
4242 }
4243 CategoryPlot that = (CategoryPlot) obj;
4244 if (this.orientation != that.orientation) {
4245 return false;
4246 }
4247 if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) {
4248 return false;
4249 }
4250 if (!this.domainAxes.equals(that.domainAxes)) {
4251 return false;
4252 }
4253 if (!this.domainAxisLocations.equals(that.domainAxisLocations)) {
4254 return false;
4255 }
4256 if (this.drawSharedDomainAxis != that.drawSharedDomainAxis) {
4257 return false;
4258 }
4259 if (!this.rangeAxes.equals(that.rangeAxes)) {
4260 return false;
4261 }
4262 if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) {
4263 return false;
4264 }
4265 if (!ObjectUtilities.equal(this.datasetToDomainAxisMap,
4266 that.datasetToDomainAxisMap)) {
4267 return false;
4268 }
4269 if (!ObjectUtilities.equal(this.datasetToRangeAxisMap,
4270 that.datasetToRangeAxisMap)) {
4271 return false;
4272 }
4273 if (!ObjectUtilities.equal(this.renderers, that.renderers)) {
4274 return false;
4275 }
4276 if (this.renderingOrder != that.renderingOrder) {
4277 return false;
4278 }
4279 if (this.columnRenderingOrder != that.columnRenderingOrder) {
4280 return false;
4281 }
4282 if (this.rowRenderingOrder != that.rowRenderingOrder) {
4283 return false;
4284 }
4285 if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
4286 return false;
4287 }
4288 if (this.domainGridlinePosition != that.domainGridlinePosition) {
4289 return false;
4290 }
4291 if (!ObjectUtilities.equal(this.domainGridlineStroke,
4292 that.domainGridlineStroke)) {
4293 return false;
4294 }
4295 if (!PaintUtilities.equal(this.domainGridlinePaint,
4296 that.domainGridlinePaint)) {
4297 return false;
4298 }
4299 if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) {
4300 return false;
4301 }
4302 if (!ObjectUtilities.equal(this.rangeGridlineStroke,
4303 that.rangeGridlineStroke)) {
4304 return false;
4305 }
4306 if (!PaintUtilities.equal(this.rangeGridlinePaint,
4307 that.rangeGridlinePaint)) {
4308 return false;
4309 }
4310 if (this.anchorValue != that.anchorValue) {
4311 return false;
4312 }
4313 if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) {
4314 return false;
4315 }
4316 if (this.rangeCrosshairValue != that.rangeCrosshairValue) {
4317 return false;
4318 }
4319 if (!ObjectUtilities.equal(this.rangeCrosshairStroke,
4320 that.rangeCrosshairStroke)) {
4321 return false;
4322 }
4323 if (!PaintUtilities.equal(this.rangeCrosshairPaint,
4324 that.rangeCrosshairPaint)) {
4325 return false;
4326 }
4327 if (this.rangeCrosshairLockedOnData
4328 != that.rangeCrosshairLockedOnData) {
4329 return false;
4330 }
4331 if (!ObjectUtilities.equal(this.foregroundDomainMarkers,
4332 that.foregroundDomainMarkers)) {
4333 return false;
4334 }
4335 if (!ObjectUtilities.equal(this.backgroundDomainMarkers,
4336 that.backgroundDomainMarkers)) {
4337 return false;
4338 }
4339 if (!ObjectUtilities.equal(this.foregroundRangeMarkers,
4340 that.foregroundRangeMarkers)) {
4341 return false;
4342 }
4343 if (!ObjectUtilities.equal(this.backgroundRangeMarkers,
4344 that.backgroundRangeMarkers)) {
4345 return false;
4346 }
4347 if (!ObjectUtilities.equal(this.annotations, that.annotations)) {
4348 return false;
4349 }
4350 if (this.weight != that.weight) {
4351 return false;
4352 }
4353 if (!ObjectUtilities.equal(this.fixedDomainAxisSpace,
4354 that.fixedDomainAxisSpace)) {
4355 return false;
4356 }
4357 if (!ObjectUtilities.equal(this.fixedRangeAxisSpace,
4358 that.fixedRangeAxisSpace)) {
4359 return false;
4360 }
4361 if (!ObjectUtilities.equal(this.fixedLegendItems,
4362 that.fixedLegendItems)) {
4363 return false;
4364 }
4365 if (this.domainCrosshairVisible != that.domainCrosshairVisible) {
4366 return false;
4367 }
4368 if (this.crosshairDatasetIndex != that.crosshairDatasetIndex) {
4369 return false;
4370 }
4371 if (!ObjectUtilities.equal(this.domainCrosshairColumnKey,
4372 that.domainCrosshairColumnKey)) {
4373 return false;
4374 }
4375 if (!ObjectUtilities.equal(this.domainCrosshairRowKey,
4376 that.domainCrosshairRowKey)) {
4377 return false;
4378 }
4379 if (!PaintUtilities.equal(this.domainCrosshairPaint,
4380 that.domainCrosshairPaint)) {
4381 return false;
4382 }
4383 if (!ObjectUtilities.equal(this.domainCrosshairStroke,
4384 that.domainCrosshairStroke)) {
4385 return false;
4386 }
4387 return super.equals(obj);
4388
4389 }
4390
4391 /**
4392 * Returns a clone of the plot.
4393 *
4394 * @return A clone.
4395 *
4396 * @throws CloneNotSupportedException if the cloning is not supported.
4397 */
4398 public Object clone() throws CloneNotSupportedException {
4399
4400 CategoryPlot clone = (CategoryPlot) super.clone();
4401
4402 clone.domainAxes = new ObjectList();
4403 for (int i = 0; i < this.domainAxes.size(); i++) {
4404 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i);
4405 if (xAxis != null) {
4406 CategoryAxis clonedAxis = (CategoryAxis) xAxis.clone();
4407 clone.setDomainAxis(i, clonedAxis);
4408 }
4409 }
4410 clone.domainAxisLocations
4411 = (ObjectList) this.domainAxisLocations.clone();
4412
4413 clone.rangeAxes = new ObjectList();
4414 for (int i = 0; i < this.rangeAxes.size(); i++) {
4415 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i);
4416 if (yAxis != null) {
4417 ValueAxis clonedAxis = (ValueAxis) yAxis.clone();
4418 clone.setRangeAxis(i, clonedAxis);
4419 }
4420 }
4421 clone.rangeAxisLocations = (ObjectList) this.rangeAxisLocations.clone();
4422
4423 clone.datasets = (ObjectList) this.datasets.clone();
4424 for (int i = 0; i < clone.datasets.size(); i++) {
4425 CategoryDataset dataset = clone.getDataset(i);
4426 if (dataset != null) {
4427 dataset.addChangeListener(clone);
4428 }
4429 }
4430 clone.datasetToDomainAxisMap
4431 = (ObjectList) this.datasetToDomainAxisMap.clone();
4432 clone.datasetToRangeAxisMap
4433 = (ObjectList) this.datasetToRangeAxisMap.clone();
4434 clone.renderers = (ObjectList) this.renderers.clone();
4435 if (this.fixedDomainAxisSpace != null) {
4436 clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone(
4437 this.fixedDomainAxisSpace);
4438 }
4439 if (this.fixedRangeAxisSpace != null) {
4440 clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone(
4441 this.fixedRangeAxisSpace);
4442 }
4443
4444 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
4445 clone.foregroundDomainMarkers = cloneMarkerMap(
4446 this.foregroundDomainMarkers);
4447 clone.backgroundDomainMarkers = cloneMarkerMap(
4448 this.backgroundDomainMarkers);
4449 clone.foregroundRangeMarkers = cloneMarkerMap(
4450 this.foregroundRangeMarkers);
4451 clone.backgroundRangeMarkers = cloneMarkerMap(
4452 this.backgroundRangeMarkers);
4453 if (this.fixedLegendItems != null) {
4454 clone.fixedLegendItems
4455 = (LegendItemCollection) this.fixedLegendItems.clone();
4456 }
4457 return clone;
4458
4459 }
4460
4461 /**
4462 * A utility method to clone the marker maps.
4463 *
4464 * @param map the map to clone.
4465 *
4466 * @return A clone of the map.
4467 *
4468 * @throws CloneNotSupportedException if there is some problem cloning the
4469 * map.
4470 */
4471 private Map cloneMarkerMap(Map map) throws CloneNotSupportedException {
4472 Map clone = new HashMap();
4473 Set keys = map.keySet();
4474 Iterator iterator = keys.iterator();
4475 while (iterator.hasNext()) {
4476 Object key = iterator.next();
4477 List entry = (List) map.get(key);
4478 Object toAdd = ObjectUtilities.deepClone(entry);
4479 clone.put(key, toAdd);
4480 }
4481 return clone;
4482 }
4483
4484 /**
4485 * Provides serialization support.
4486 *
4487 * @param stream the output stream.
4488 *
4489 * @throws IOException if there is an I/O error.
4490 */
4491 private void writeObject(ObjectOutputStream stream) throws IOException {
4492 stream.defaultWriteObject();
4493 SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
4494 SerialUtilities.writePaint(this.domainGridlinePaint, stream);
4495 SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
4496 SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
4497 SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream);
4498 SerialUtilities.writePaint(this.rangeCrosshairPaint, stream);
4499 SerialUtilities.writeStroke(this.domainCrosshairStroke, stream);
4500 SerialUtilities.writePaint(this.domainCrosshairPaint, stream);
4501 }
4502
4503 /**
4504 * Provides serialization support.
4505 *
4506 * @param stream the input stream.
4507 *
4508 * @throws IOException if there is an I/O error.
4509 * @throws ClassNotFoundException if there is a classpath problem.
4510 */
4511 private void readObject(ObjectInputStream stream)
4512 throws IOException, ClassNotFoundException {
4513
4514 stream.defaultReadObject();
4515 this.domainGridlineStroke = SerialUtilities.readStroke(stream);
4516 this.domainGridlinePaint = SerialUtilities.readPaint(stream);
4517 this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
4518 this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
4519 this.rangeCrosshairStroke = SerialUtilities.readStroke(stream);
4520 this.rangeCrosshairPaint = SerialUtilities.readPaint(stream);
4521 this.domainCrosshairStroke = SerialUtilities.readStroke(stream);
4522 this.domainCrosshairPaint = SerialUtilities.readPaint(stream);
4523
4524 for (int i = 0; i < this.domainAxes.size(); i++) {
4525 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i);
4526 if (xAxis != null) {
4527 xAxis.setPlot(this);
4528 xAxis.addChangeListener(this);
4529 }
4530 }
4531 for (int i = 0; i < this.rangeAxes.size(); i++) {
4532 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i);
4533 if (yAxis != null) {
4534 yAxis.setPlot(this);
4535 yAxis.addChangeListener(this);
4536 }
4537 }
4538 int datasetCount = this.datasets.size();
4539 for (int i = 0; i < datasetCount; i++) {
4540 Dataset dataset = (Dataset) this.datasets.get(i);
4541 if (dataset != null) {
4542 dataset.addChangeListener(this);
4543 }
4544 }
4545 int rendererCount = this.renderers.size();
4546 for (int i = 0; i < rendererCount; i++) {
4547 CategoryItemRenderer renderer
4548 = (CategoryItemRenderer) this.renderers.get(i);
4549 if (renderer != null) {
4550 renderer.addChangeListener(this);
4551 }
4552 }
4553
4554 }
4555
4556 }