001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * -----------------
028 * CategoryPlot.java
029 * -----------------
030 * (C) Copyright 2000-2007, 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 *
036 * $Id: CategoryPlot.java,v 1.23.2.17 2007/06/07 12:49:36 mungady Exp $
037 *
038 * Changes (from 21-Jun-2001)
039 * --------------------------
040 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
041 * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
042 * 18-Sep-2001 : Updated header (DG);
043 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
044 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
045 * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
046 * available space rather than a fixed number of units (DG);
047 * 12-Dec-2001 : Changed constructors to protected (DG);
048 * 13-Dec-2001 : Added tooltips (DG);
049 * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
050 * some argument checking code. Thanks to Taoufik Romdhane for
051 * suggesting this (DG);
052 * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
053 * alpha-transparency for Plot and subclasses (DG);
054 * 06-Mar-2002 : Updated import statements (DG);
055 * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
056 * to use the CategoryItemRenderer interface (DG);
057 * 22-Mar-2002 : Dropped the getCategories() method (DG);
058 * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
059 * class (DG);
060 * 29-Apr-2002 : New methods to support printing values at the end of bars,
061 * contributed by Jeremy Bowman (DG);
062 * 11-May-2002 : New methods for label visibility and overlaid plot support,
063 * contributed by Jeremy Bowman (DG);
064 * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
065 * renderer. Moved constants into the CategoryPlotConstants
066 * interface. Updated Javadoc comments (DG);
067 * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
068 * lower bound on the range axis (if necessary), updated
069 * Javadocs (DG);
070 * 25-Jun-2002 : Removed redundant imports (DG);
071 * 20-Aug-2002 : Changed the constructor for Marker (DG);
072 * 28-Aug-2002 : Added listener notification to setDomainAxis() and
073 * setRangeAxis() (DG);
074 * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
075 * Checkstyle (DG);
076 * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
077 * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
078 * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
079 * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
080 * these were set in the axes) (DG);
081 * 19-Nov-2002 : Added axis location parameters to constructor (DG);
082 * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
083 * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
084 * 26-Mar-2003 : Implemented Serializable (DG);
085 * 02-May-2003 : Moved render() method up from subclasses. Added secondary
086 * range markers. Added an attribute to control the dataset
087 * rendering order. Added a drawAnnotations() method. Changed
088 * the axis location from an int to an AxisLocation (DG);
089 * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
090 * this class (DG);
091 * 02-Jun-2003 : Removed check for range axis compatibility (DG);
092 * 04-Jul-2003 : Added a domain gridline position attribute (DG);
093 * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
094 * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
095 * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
096 * changes) (DG);
097 * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
098 * 790407 (initialise method) (DG);
099 * 08-Sep-2003 : Added internationalization via use of properties
100 * resourceBundle (RFE 690236) (AL);
101 * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed
102 * ValueAxis API (DG);
103 * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
104 * 15-Sep-2003 : Fixed two bugs in serialization, implemented
105 * PublicCloneable (DG);
106 * 23-Oct-2003 : Added event notification for changes to renderer (DG);
107 * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
108 * 03-Dec-2003 : Modified draw method to accept anchor (DG);
109 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
110 * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
111 * stacked (DG);
112 * 12-May-2004 : Added fixed legend items (DG);
113 * 19-May-2004 : Added check for null legend item from renderer (DG);
114 * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
115 * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
116 * --> datasetsMappedToRangeAxis(), and ensured that returned
117 * list doesn't contain null datasets (DG);
118 * 12-Nov-2004 : Implemented new Zoomable interface (DG);
119 * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
120 * CategoryItemRenderer (DG);
121 * 04-May-2005 : Fixed serialization of range markers (DG);
122 * 05-May-2005 : Updated draw() method parameters (DG);
123 * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
124 * RFE 1183100 (DG);
125 * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
126 * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
127 * 02-Jun-2005 : Added support for domain markers (DG);
128 * 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG);
129 * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
130 * 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to
131 * match XYPlot (see RFE 1220495) (DG);
132 * ------------- JFREECHART 1.0.x ---------------------------------------------
133 * 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the
134 * renderer might influence the axis range (DG);
135 * 27-Jan-2006 : Added various null argument checks (DG);
136 * 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing
137 * category labels, thanks to Adriaan Joubert (1277726) (DG);
138 * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
139 * 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and
140 * getCategoriesForAxis() methods (DG);
141 * 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and
142 * setRowRenderingOrder() (DG);
143 * 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data
144 * area) (DG);
145 * 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument
146 * ignored) (DG);
147 * 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and
148 * setRangeCrosshairStroke(), fixed clipping for
149 * anntotations (DG);
150 * 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG);
151 *
152 */
153
154 package org.jfree.chart.plot;
155
156 import java.awt.AlphaComposite;
157 import java.awt.BasicStroke;
158 import java.awt.Color;
159 import java.awt.Composite;
160 import java.awt.Font;
161 import java.awt.Graphics2D;
162 import java.awt.Paint;
163 import java.awt.Shape;
164 import java.awt.Stroke;
165 import java.awt.geom.Line2D;
166 import java.awt.geom.Point2D;
167 import java.awt.geom.Rectangle2D;
168 import java.io.IOException;
169 import java.io.ObjectInputStream;
170 import java.io.ObjectOutputStream;
171 import java.io.Serializable;
172 import java.util.ArrayList;
173 import java.util.Collection;
174 import java.util.Collections;
175 import java.util.HashMap;
176 import java.util.Iterator;
177 import java.util.List;
178 import java.util.Map;
179 import java.util.ResourceBundle;
180 import java.util.Set;
181
182 import org.jfree.chart.LegendItem;
183 import org.jfree.chart.LegendItemCollection;
184 import org.jfree.chart.annotations.CategoryAnnotation;
185 import org.jfree.chart.axis.Axis;
186 import org.jfree.chart.axis.AxisCollection;
187 import org.jfree.chart.axis.AxisLocation;
188 import org.jfree.chart.axis.AxisSpace;
189 import org.jfree.chart.axis.AxisState;
190 import org.jfree.chart.axis.CategoryAnchor;
191 import org.jfree.chart.axis.CategoryAxis;
192 import org.jfree.chart.axis.ValueAxis;
193 import org.jfree.chart.axis.ValueTick;
194 import org.jfree.chart.event.ChartChangeEventType;
195 import org.jfree.chart.event.PlotChangeEvent;
196 import org.jfree.chart.event.RendererChangeEvent;
197 import org.jfree.chart.event.RendererChangeListener;
198 import org.jfree.chart.renderer.category.CategoryItemRenderer;
199 import org.jfree.chart.renderer.category.CategoryItemRendererState;
200 import org.jfree.data.Range;
201 import org.jfree.data.category.CategoryDataset;
202 import org.jfree.data.general.Dataset;
203 import org.jfree.data.general.DatasetChangeEvent;
204 import org.jfree.data.general.DatasetUtilities;
205 import org.jfree.io.SerialUtilities;
206 import org.jfree.ui.Layer;
207 import org.jfree.ui.RectangleEdge;
208 import org.jfree.ui.RectangleInsets;
209 import org.jfree.util.ObjectList;
210 import org.jfree.util.ObjectUtilities;
211 import org.jfree.util.PaintUtilities;
212 import org.jfree.util.PublicCloneable;
213 import org.jfree.util.SortOrder;
214
215 /**
216 * A general plotting class that uses data from a {@link CategoryDataset} and
217 * renders each data item using a {@link CategoryItemRenderer}.
218 */
219 public class CategoryPlot extends Plot
220 implements ValueAxisPlot,
221 Zoomable,
222 RendererChangeListener,
223 Cloneable, PublicCloneable, Serializable {
224
225 /** For serialization. */
226 private static final long serialVersionUID = -3537691700434728188L;
227
228 /**
229 * The default visibility of the grid lines plotted against the domain
230 * axis.
231 */
232 public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
233
234 /**
235 * The default visibility of the grid lines plotted against the range
236 * axis.
237 */
238 public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
239
240 /** The default grid line stroke. */
241 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
242 BasicStroke.CAP_BUTT,
243 BasicStroke.JOIN_BEVEL,
244 0.0f,
245 new float[] {2.0f, 2.0f},
246 0.0f);
247
248 /** The default grid line paint. */
249 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
250
251 /** The default value label font. */
252 public static final Font DEFAULT_VALUE_LABEL_FONT
253 = new Font("SansSerif", Font.PLAIN, 10);
254
255 /**
256 * The default crosshair visibility.
257 *
258 * @since 1.0.5
259 */
260 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
261
262 /**
263 * The default crosshair stroke.
264 *
265 * @since 1.0.5
266 */
267 public static final Stroke DEFAULT_CROSSHAIR_STROKE
268 = DEFAULT_GRIDLINE_STROKE;
269
270 /**
271 * The default crosshair paint.
272 *
273 * @since 1.0.5
274 */
275 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
276
277 /** The resourceBundle for the localization. */
278 protected static ResourceBundle localizationResources
279 = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
280
281 /** The plot orientation. */
282 private PlotOrientation orientation;
283
284 /** The offset between the data area and the axes. */
285 private RectangleInsets axisOffset;
286
287 /** Storage for the domain axes. */
288 private ObjectList domainAxes;
289
290 /** Storage for the domain axis locations. */
291 private ObjectList domainAxisLocations;
292
293 /**
294 * A flag that controls whether or not the shared domain axis is drawn
295 * (only relevant when the plot is being used as a subplot).
296 */
297 private boolean drawSharedDomainAxis;
298
299 /** Storage for the range axes. */
300 private ObjectList rangeAxes;
301
302 /** Storage for the range axis locations. */
303 private ObjectList rangeAxisLocations;
304
305 /** Storage for the datasets. */
306 private ObjectList datasets;
307
308 /** Storage for keys that map datasets to domain axes. */
309 private ObjectList datasetToDomainAxisMap;
310
311 /** Storage for keys that map datasets to range axes. */
312 private ObjectList datasetToRangeAxisMap;
313
314 /** Storage for the renderers. */
315 private ObjectList renderers;
316
317 /** The dataset rendering order. */
318 private DatasetRenderingOrder renderingOrder
319 = DatasetRenderingOrder.REVERSE;
320
321 /**
322 * Controls the order in which the columns are traversed when rendering the
323 * data items.
324 */
325 private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
326
327 /**
328 * Controls the order in which the rows are traversed when rendering the
329 * data items.
330 */
331 private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
332
333 /**
334 * A flag that controls whether the grid-lines for the domain axis are
335 * visible.
336 */
337 private boolean domainGridlinesVisible;
338
339 /** The position of the domain gridlines relative to the category. */
340 private CategoryAnchor domainGridlinePosition;
341
342 /** The stroke used to draw the domain grid-lines. */
343 private transient Stroke domainGridlineStroke;
344
345 /** The paint used to draw the domain grid-lines. */
346 private transient Paint domainGridlinePaint;
347
348 /**
349 * A flag that controls whether the grid-lines for the range axis are
350 * visible.
351 */
352 private boolean rangeGridlinesVisible;
353
354 /** The stroke used to draw the range axis grid-lines. */
355 private transient Stroke rangeGridlineStroke;
356
357 /** The paint used to draw the range axis grid-lines. */
358 private transient Paint rangeGridlinePaint;
359
360 /** The anchor value. */
361 private double anchorValue;
362
363 /** A flag that controls whether or not a range crosshair is drawn. */
364 private boolean rangeCrosshairVisible;
365
366 /** The range crosshair value. */
367 private double rangeCrosshairValue;
368
369 /** The pen/brush used to draw the crosshair (if any). */
370 private transient Stroke rangeCrosshairStroke;
371
372 /** The color used to draw the crosshair (if any). */
373 private transient Paint rangeCrosshairPaint;
374
375 /**
376 * A flag that controls whether or not the crosshair locks onto actual
377 * data points.
378 */
379 private boolean rangeCrosshairLockedOnData = true;
380
381 /** A map containing lists of markers for the domain axes. */
382 private Map foregroundDomainMarkers;
383
384 /** A map containing lists of markers for the domain axes. */
385 private Map backgroundDomainMarkers;
386
387 /** A map containing lists of markers for the range axes. */
388 private Map foregroundRangeMarkers;
389
390 /** A map containing lists of markers for the range axes. */
391 private Map backgroundRangeMarkers;
392
393 /**
394 * A (possibly empty) list of annotations for the plot. The list should
395 * be initialised in the constructor and never allowed to be
396 * <code>null</code>.
397 */
398 private List annotations;
399
400 /**
401 * The weight for the plot (only relevant when the plot is used as a subplot
402 * within a combined plot).
403 */
404 private int weight;
405
406 /** The fixed space for the domain axis. */
407 private AxisSpace fixedDomainAxisSpace;
408
409 /** The fixed space for the range axis. */
410 private AxisSpace fixedRangeAxisSpace;
411
412 /**
413 * An optional collection of legend items that can be returned by the
414 * getLegendItems() method.
415 */
416 private LegendItemCollection fixedLegendItems;
417
418 /**
419 * Default constructor.
420 */
421 public CategoryPlot() {
422 this(null, null, null, null);
423 }
424
425 /**
426 * Creates a new plot.
427 *
428 * @param dataset the dataset (<code>null</code> permitted).
429 * @param domainAxis the domain axis (<code>null</code> permitted).
430 * @param rangeAxis the range axis (<code>null</code> permitted).
431 * @param renderer the item renderer (<code>null</code> permitted).
432 *
433 */
434 public CategoryPlot(CategoryDataset dataset,
435 CategoryAxis domainAxis,
436 ValueAxis rangeAxis,
437 CategoryItemRenderer renderer) {
438
439 super();
440
441 this.orientation = PlotOrientation.VERTICAL;
442
443 // allocate storage for dataset, axes and renderers
444 this.domainAxes = new ObjectList();
445 this.domainAxisLocations = new ObjectList();
446 this.rangeAxes = new ObjectList();
447 this.rangeAxisLocations = new ObjectList();
448
449 this.datasetToDomainAxisMap = new ObjectList();
450 this.datasetToRangeAxisMap = new ObjectList();
451
452 this.renderers = new ObjectList();
453
454 this.datasets = new ObjectList();
455 this.datasets.set(0, dataset);
456 if (dataset != null) {
457 dataset.addChangeListener(this);
458 }
459
460 this.axisOffset = RectangleInsets.ZERO_INSETS;
461
462 setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
463 setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
464
465 this.renderers.set(0, renderer);
466 if (renderer != null) {
467 renderer.setPlot(this);
468 renderer.addChangeListener(this);
469 }
470
471 this.domainAxes.set(0, domainAxis);
472 this.mapDatasetToDomainAxis(0, 0);
473 if (domainAxis != null) {
474 domainAxis.setPlot(this);
475 domainAxis.addChangeListener(this);
476 }
477 this.drawSharedDomainAxis = false;
478
479 this.rangeAxes.set(0, rangeAxis);
480 this.mapDatasetToRangeAxis(0, 0);
481 if (rangeAxis != null) {
482 rangeAxis.setPlot(this);
483 rangeAxis.addChangeListener(this);
484 }
485
486 configureDomainAxes();
487 configureRangeAxes();
488
489 this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
490 this.domainGridlinePosition = CategoryAnchor.MIDDLE;
491 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
492 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
493
494 this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
495 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
496 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
497
498 this.foregroundDomainMarkers = new HashMap();
499 this.backgroundDomainMarkers = new HashMap();
500 this.foregroundRangeMarkers = new HashMap();
501 this.backgroundRangeMarkers = new HashMap();
502
503 Marker baseline = new ValueMarker(0.0, new Color(0.8f, 0.8f, 0.8f,
504 0.5f), new BasicStroke(1.0f), new Color(0.85f, 0.85f, 0.95f,
505 0.5f), new BasicStroke(1.0f), 0.6f);
506 addRangeMarker(baseline, Layer.BACKGROUND);
507
508 this.anchorValue = 0.0;
509
510 this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE;
511 this.rangeCrosshairValue = 0.0;
512 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
513 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
514
515 this.annotations = new java.util.ArrayList();
516
517 }
518
519 /**
520 * Returns a string describing the type of plot.
521 *
522 * @return The type.
523 */
524 public String getPlotType() {
525 return localizationResources.getString("Category_Plot");
526 }
527
528 /**
529 * Returns the orientation of the plot.
530 *
531 * @return The orientation of the plot (never <code>null</code>).
532 *
533 * @see #setOrientation(PlotOrientation)
534 */
535 public PlotOrientation getOrientation() {
536 return this.orientation;
537 }
538
539 /**
540 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
541 * all registered listeners.
542 *
543 * @param orientation the orientation (<code>null</code> not permitted).
544 *
545 * @see #getOrientation()
546 */
547 public void setOrientation(PlotOrientation orientation) {
548 if (orientation == null) {
549 throw new IllegalArgumentException("Null 'orientation' argument.");
550 }
551 this.orientation = orientation;
552 notifyListeners(new PlotChangeEvent(this));
553 }
554
555 /**
556 * Returns the axis offset.
557 *
558 * @return The axis offset (never <code>null</code>).
559 *
560 * @see #setAxisOffset(RectangleInsets)
561 */
562 public RectangleInsets getAxisOffset() {
563 return this.axisOffset;
564 }
565
566 /**
567 * Sets the axis offsets (gap between the data area and the axes) and
568 * sends a {@link PlotChangeEvent} to all registered listeners.
569 *
570 * @param offset the offset (<code>null</code> not permitted).
571 *
572 * @see #getAxisOffset()
573 */
574 public void setAxisOffset(RectangleInsets offset) {
575 if (offset == null) {
576 throw new IllegalArgumentException("Null 'offset' argument.");
577 }
578 this.axisOffset = offset;
579 notifyListeners(new PlotChangeEvent(this));
580 }
581
582 /**
583 * Returns the domain axis for the plot. If the domain axis for this plot
584 * is <code>null</code>, then the method will return the parent plot's
585 * domain axis (if there is a parent plot).
586 *
587 * @return The domain axis (<code>null</code> permitted).
588 *
589 * @see #setDomainAxis(CategoryAxis)
590 */
591 public CategoryAxis getDomainAxis() {
592 return getDomainAxis(0);
593 }
594
595 /**
596 * Returns a domain axis.
597 *
598 * @param index the axis index.
599 *
600 * @return The axis (<code>null</code> possible).
601 *
602 * @see #setDomainAxis(int, CategoryAxis)
603 */
604 public CategoryAxis getDomainAxis(int index) {
605 CategoryAxis result = null;
606 if (index < this.domainAxes.size()) {
607 result = (CategoryAxis) this.domainAxes.get(index);
608 }
609 if (result == null) {
610 Plot parent = getParent();
611 if (parent instanceof CategoryPlot) {
612 CategoryPlot cp = (CategoryPlot) parent;
613 result = cp.getDomainAxis(index);
614 }
615 }
616 return result;
617 }
618
619 /**
620 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
621 * all registered listeners.
622 *
623 * @param axis the axis (<code>null</code> permitted).
624 *
625 * @see #getDomainAxis()
626 */
627 public void setDomainAxis(CategoryAxis axis) {
628 setDomainAxis(0, axis);
629 }
630
631 /**
632 * Sets a domain axis and sends a {@link PlotChangeEvent} to all
633 * registered listeners.
634 *
635 * @param index the axis index.
636 * @param axis the axis (<code>null</code> permitted).
637 *
638 * @see #getDomainAxis(int)
639 */
640 public void setDomainAxis(int index, CategoryAxis axis) {
641 setDomainAxis(index, axis, true);
642 }
643
644 /**
645 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
646 * all registered listeners.
647 *
648 * @param index the axis index.
649 * @param axis the axis (<code>null</code> permitted).
650 * @param notify notify listeners?
651 */
652 public void setDomainAxis(int index, CategoryAxis axis, boolean notify) {
653 CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
654 if (existing != null) {
655 existing.removeChangeListener(this);
656 }
657 if (axis != null) {
658 axis.setPlot(this);
659 }
660 this.domainAxes.set(index, axis);
661 if (axis != null) {
662 axis.configure();
663 axis.addChangeListener(this);
664 }
665 if (notify) {
666 notifyListeners(new PlotChangeEvent(this));
667 }
668 }
669
670 /**
671 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
672 * to all registered listeners.
673 *
674 * @param axes the axes (<code>null</code> not permitted).
675 *
676 * @see #setRangeAxes(ValueAxis[])
677 */
678 public void setDomainAxes(CategoryAxis[] axes) {
679 for (int i = 0; i < axes.length; i++) {
680 setDomainAxis(i, axes[i], false);
681 }
682 notifyListeners(new PlotChangeEvent(this));
683 }
684
685 /**
686 * Returns the index of the specified axis, or <code>-1</code> if the axis
687 * is not assigned to the plot.
688 *
689 * @param axis the axis.
690 *
691 * @return The axis index.
692 *
693 * @since 1.0.3
694 */
695 public int getDomainAxisIndex(CategoryAxis axis) {
696 return this.domainAxes.indexOf(axis);
697 }
698
699 /**
700 * Returns the domain axis location for the primary domain axis.
701 *
702 * @return The location (never <code>null</code>).
703 *
704 * @see #getRangeAxisLocation()
705 */
706 public AxisLocation getDomainAxisLocation() {
707 return getDomainAxisLocation(0);
708 }
709
710 /**
711 * Returns the location for a domain axis.
712 *
713 * @param index the axis index.
714 *
715 * @return The location.
716 *
717 * @see #setDomainAxisLocation(int, AxisLocation)
718 */
719 public AxisLocation getDomainAxisLocation(int index) {
720 AxisLocation result = null;
721 if (index < this.domainAxisLocations.size()) {
722 result = (AxisLocation) this.domainAxisLocations.get(index);
723 }
724 if (result == null) {
725 result = AxisLocation.getOpposite(getDomainAxisLocation(0));
726 }
727 return result;
728 }
729
730 /**
731 * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
732 * to all registered listeners.
733 *
734 * @param location the axis location (<code>null</code> not permitted).
735 *
736 * @see #getDomainAxisLocation()
737 * @see #setDomainAxisLocation(int, AxisLocation)
738 */
739 public void setDomainAxisLocation(AxisLocation location) {
740 // delegate...
741 setDomainAxisLocation(0, location, true);
742 }
743
744 /**
745 * Sets the location of the domain axis and, if requested, sends a
746 * {@link PlotChangeEvent} to all registered listeners.
747 *
748 * @param location the axis location (<code>null</code> not permitted).
749 * @param notify a flag that controls whether listeners are notified.
750 */
751 public void setDomainAxisLocation(AxisLocation location, boolean notify) {
752 // delegate...
753 setDomainAxisLocation(0, location, notify);
754 }
755
756 /**
757 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
758 * to all registered listeners.
759 *
760 * @param index the axis index.
761 * @param location the location.
762 *
763 * @see #getDomainAxisLocation(int)
764 * @see #setRangeAxisLocation(int, AxisLocation)
765 */
766 public void setDomainAxisLocation(int index, AxisLocation location) {
767 // delegate...
768 setDomainAxisLocation(index, location, true);
769 }
770
771 /**
772 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
773 * to all registered listeners.
774 *
775 * @param index the axis index.
776 * @param location the location.
777 * @param notify notify listeners?
778 *
779 * @since 1.0.5
780 *
781 * @see #getDomainAxisLocation(int)
782 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
783 */
784 public void setDomainAxisLocation(int index, AxisLocation location,
785 boolean notify) {
786 if (index == 0 && location == null) {
787 throw new IllegalArgumentException(
788 "Null 'location' for index 0 not permitted.");
789 }
790 this.domainAxisLocations.set(index, location);
791 if (notify) {
792 notifyListeners(new PlotChangeEvent(this));
793 }
794 }
795
796 /**
797 * Returns the domain axis edge. This is derived from the axis location
798 * and the plot orientation.
799 *
800 * @return The edge (never <code>null</code>).
801 */
802 public RectangleEdge getDomainAxisEdge() {
803 return getDomainAxisEdge(0);
804 }
805
806 /**
807 * Returns the edge for a domain axis.
808 *
809 * @param index the axis index.
810 *
811 * @return The edge (never <code>null</code>).
812 */
813 public RectangleEdge getDomainAxisEdge(int index) {
814 RectangleEdge result = null;
815 AxisLocation location = getDomainAxisLocation(index);
816 if (location != null) {
817 result = Plot.resolveDomainAxisLocation(location, this.orientation);
818 }
819 else {
820 result = RectangleEdge.opposite(getDomainAxisEdge(0));
821 }
822 return result;
823 }
824
825 /**
826 * Returns the number of domain axes.
827 *
828 * @return The axis count.
829 */
830 public int getDomainAxisCount() {
831 return this.domainAxes.size();
832 }
833
834 /**
835 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
836 * to all registered listeners.
837 */
838 public void clearDomainAxes() {
839 for (int i = 0; i < this.domainAxes.size(); i++) {
840 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
841 if (axis != null) {
842 axis.removeChangeListener(this);
843 }
844 }
845 this.domainAxes.clear();
846 notifyListeners(new PlotChangeEvent(this));
847 }
848
849 /**
850 * Configures the domain axes.
851 */
852 public void configureDomainAxes() {
853 for (int i = 0; i < this.domainAxes.size(); i++) {
854 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
855 if (axis != null) {
856 axis.configure();
857 }
858 }
859 }
860
861 /**
862 * Returns the range axis for the plot. If the range axis for this plot is
863 * null, then the method will return the parent plot's range axis (if there
864 * is a parent plot).
865 *
866 * @return The range axis (possibly <code>null</code>).
867 */
868 public ValueAxis getRangeAxis() {
869 return getRangeAxis(0);
870 }
871
872 /**
873 * Returns a range axis.
874 *
875 * @param index the axis index.
876 *
877 * @return The axis (<code>null</code> possible).
878 */
879 public ValueAxis getRangeAxis(int index) {
880 ValueAxis result = null;
881 if (index < this.rangeAxes.size()) {
882 result = (ValueAxis) this.rangeAxes.get(index);
883 }
884 if (result == null) {
885 Plot parent = getParent();
886 if (parent instanceof CategoryPlot) {
887 CategoryPlot cp = (CategoryPlot) parent;
888 result = cp.getRangeAxis(index);
889 }
890 }
891 return result;
892 }
893
894 /**
895 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
896 * all registered listeners.
897 *
898 * @param axis the axis (<code>null</code> permitted).
899 */
900 public void setRangeAxis(ValueAxis axis) {
901 setRangeAxis(0, axis);
902 }
903
904 /**
905 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
906 * listeners.
907 *
908 * @param index the axis index.
909 * @param axis the axis.
910 */
911 public void setRangeAxis(int index, ValueAxis axis) {
912 setRangeAxis(index, axis, true);
913 }
914
915 /**
916 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
917 * all registered listeners.
918 *
919 * @param index the axis index.
920 * @param axis the axis.
921 * @param notify notify listeners?
922 */
923 public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
924 ValueAxis existing = (ValueAxis) this.rangeAxes.get(index);
925 if (existing != null) {
926 existing.removeChangeListener(this);
927 }
928 if (axis != null) {
929 axis.setPlot(this);
930 }
931 this.rangeAxes.set(index, axis);
932 if (axis != null) {
933 axis.configure();
934 axis.addChangeListener(this);
935 }
936 if (notify) {
937 notifyListeners(new PlotChangeEvent(this));
938 }
939 }
940
941 /**
942 * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
943 * to all registered listeners.
944 *
945 * @param axes the axes (<code>null</code> not permitted).
946 *
947 * @see #setDomainAxes(CategoryAxis[])
948 */
949 public void setRangeAxes(ValueAxis[] axes) {
950 for (int i = 0; i < axes.length; i++) {
951 setRangeAxis(i, axes[i], false);
952 }
953 notifyListeners(new PlotChangeEvent(this));
954 }
955
956 /**
957 * Returns the range axis location.
958 *
959 * @return The location (never <code>null</code>).
960 */
961 public AxisLocation getRangeAxisLocation() {
962 return getRangeAxisLocation(0);
963 }
964
965 /**
966 * Returns the location for a range axis.
967 *
968 * @param index the axis index.
969 *
970 * @return The location.
971 *
972 * @see #setRangeAxisLocation(int, AxisLocation)
973 */
974 public AxisLocation getRangeAxisLocation(int index) {
975 AxisLocation result = null;
976 if (index < this.rangeAxisLocations.size()) {
977 result = (AxisLocation) this.rangeAxisLocations.get(index);
978 }
979 if (result == null) {
980 result = AxisLocation.getOpposite(getRangeAxisLocation(0));
981 }
982 return result;
983 }
984
985 /**
986 * Sets the location of the range axis and sends a {@link PlotChangeEvent}
987 * to all registered listeners.
988 *
989 * @param location the location (<code>null</code> not permitted).
990 *
991 * @see #setRangeAxisLocation(AxisLocation, boolean)
992 * @see #setDomainAxisLocation(AxisLocation)
993 */
994 public void setRangeAxisLocation(AxisLocation location) {
995 // defer argument checking...
996 setRangeAxisLocation(location, true);
997 }
998
999 /**
1000 * Sets the location of the range axis and, if requested, sends a
1001 * {@link PlotChangeEvent} to all registered listeners.
1002 *
1003 * @param location the location (<code>null</code> not permitted).
1004 * @param notify notify listeners?
1005 *
1006 * @see #setDomainAxisLocation(AxisLocation, boolean)
1007 */
1008 public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1009 setRangeAxisLocation(0, location, notify);
1010 }
1011
1012 /**
1013 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1014 * to all registered listeners.
1015 *
1016 * @param index the axis index.
1017 * @param location the location.
1018 *
1019 * @see #getRangeAxisLocation(int)
1020 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
1021 */
1022 public void setRangeAxisLocation(int index, AxisLocation location) {
1023 setRangeAxisLocation(index, location, true);
1024 }
1025
1026 /**
1027 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1028 * to all registered listeners.
1029 *
1030 * @param index the axis index.
1031 * @param location the location.
1032 * @param notify notify listeners?
1033 *
1034 * @see #getRangeAxisLocation(int)
1035 * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1036 */
1037 public void setRangeAxisLocation(int index, AxisLocation location,
1038 boolean notify) {
1039 if (index == 0 && location == null) {
1040 throw new IllegalArgumentException(
1041 "Null 'location' for index 0 not permitted.");
1042 }
1043 this.rangeAxisLocations.set(index, location);
1044 if (notify) {
1045 notifyListeners(new PlotChangeEvent(this));
1046 }
1047 }
1048
1049 /**
1050 * Returns the edge where the primary range axis is located.
1051 *
1052 * @return The edge (never <code>null</code>).
1053 */
1054 public RectangleEdge getRangeAxisEdge() {
1055 return getRangeAxisEdge(0);
1056 }
1057
1058 /**
1059 * Returns the edge for a range axis.
1060 *
1061 * @param index the axis index.
1062 *
1063 * @return The edge.
1064 */
1065 public RectangleEdge getRangeAxisEdge(int index) {
1066 AxisLocation location = getRangeAxisLocation(index);
1067 RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1068 this.orientation);
1069 if (result == null) {
1070 result = RectangleEdge.opposite(getRangeAxisEdge(0));
1071 }
1072 return result;
1073 }
1074
1075 /**
1076 * Returns the number of range axes.
1077 *
1078 * @return The axis count.
1079 */
1080 public int getRangeAxisCount() {
1081 return this.rangeAxes.size();
1082 }
1083
1084 /**
1085 * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1086 * to all registered listeners.
1087 */
1088 public void clearRangeAxes() {
1089 for (int i = 0; i < this.rangeAxes.size(); i++) {
1090 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1091 if (axis != null) {
1092 axis.removeChangeListener(this);
1093 }
1094 }
1095 this.rangeAxes.clear();
1096 notifyListeners(new PlotChangeEvent(this));
1097 }
1098
1099 /**
1100 * Configures the range axes.
1101 */
1102 public void configureRangeAxes() {
1103 for (int i = 0; i < this.rangeAxes.size(); i++) {
1104 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1105 if (axis != null) {
1106 axis.configure();
1107 }
1108 }
1109 }
1110
1111 /**
1112 * Returns the primary dataset for the plot.
1113 *
1114 * @return The primary dataset (possibly <code>null</code>).
1115 *
1116 * @see #setDataset(CategoryDataset)
1117 */
1118 public CategoryDataset getDataset() {
1119 return getDataset(0);
1120 }
1121
1122 /**
1123 * Returns the dataset at the given index.
1124 *
1125 * @param index the dataset index.
1126 *
1127 * @return The dataset (possibly <code>null</code>).
1128 *
1129 * @see #setDataset(int, CategoryDataset)
1130 */
1131 public CategoryDataset getDataset(int index) {
1132 CategoryDataset result = null;
1133 if (this.datasets.size() > index) {
1134 result = (CategoryDataset) this.datasets.get(index);
1135 }
1136 return result;
1137 }
1138
1139 /**
1140 * Sets the dataset for the plot, replacing the existing dataset, if there
1141 * is one. This method also calls the
1142 * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
1143 * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
1144 * registered listeners.
1145 *
1146 * @param dataset the dataset (<code>null</code> permitted).
1147 *
1148 * @see #getDataset()
1149 */
1150 public void setDataset(CategoryDataset dataset) {
1151 setDataset(0, dataset);
1152 }
1153
1154 /**
1155 * Sets a dataset for the plot.
1156 *
1157 * @param index the dataset index.
1158 * @param dataset the dataset (<code>null</code> permitted).
1159 *
1160 * @see #getDataset(int)
1161 */
1162 public void setDataset(int index, CategoryDataset dataset) {
1163
1164 CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
1165 if (existing != null) {
1166 existing.removeChangeListener(this);
1167 }
1168 this.datasets.set(index, dataset);
1169 if (dataset != null) {
1170 dataset.addChangeListener(this);
1171 }
1172
1173 // send a dataset change event to self...
1174 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1175 datasetChanged(event);
1176
1177 }
1178
1179 /**
1180 * Returns the number of datasets.
1181 *
1182 * @return The number of datasets.
1183 *
1184 * @since 1.0.2
1185 */
1186 public int getDatasetCount() {
1187 return this.datasets.size();
1188 }
1189
1190 /**
1191 * Maps a dataset to a particular domain axis.
1192 *
1193 * @param index the dataset index (zero-based).
1194 * @param axisIndex the axis index (zero-based).
1195 *
1196 * @see #getDomainAxisForDataset(int)
1197 */
1198 public void mapDatasetToDomainAxis(int index, int axisIndex) {
1199 this.datasetToDomainAxisMap.set(index, new Integer(axisIndex));
1200 // fake a dataset change event to update axes...
1201 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1202 }
1203
1204 /**
1205 * Returns the domain axis for a dataset. You can change the axis for a
1206 * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
1207 *
1208 * @param index the dataset index.
1209 *
1210 * @return The domain axis.
1211 *
1212 * @see #mapDatasetToDomainAxis(int, int)
1213 */
1214 public CategoryAxis getDomainAxisForDataset(int index) {
1215 CategoryAxis result = getDomainAxis();
1216 Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(index);
1217 if (axisIndex != null) {
1218 result = getDomainAxis(axisIndex.intValue());
1219 }
1220 return result;
1221 }
1222
1223 /**
1224 * Maps a dataset to a particular range axis.
1225 *
1226 * @param index the dataset index (zero-based).
1227 * @param axisIndex the axis index (zero-based).
1228 *
1229 * @see #getRangeAxisForDataset(int)
1230 */
1231 public void mapDatasetToRangeAxis(int index, int axisIndex) {
1232 this.datasetToRangeAxisMap.set(index, new Integer(axisIndex));
1233 // fake a dataset change event to update axes...
1234 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1235 }
1236
1237 /**
1238 * Returns the range axis for a dataset. You can change the axis for a
1239 * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
1240 *
1241 * @param index the dataset index.
1242 *
1243 * @return The range axis.
1244 *
1245 * @see #mapDatasetToRangeAxis(int, int)
1246 */
1247 public ValueAxis getRangeAxisForDataset(int index) {
1248 ValueAxis result = getRangeAxis();
1249 Integer axisIndex = (Integer) this.datasetToRangeAxisMap.get(index);
1250 if (axisIndex != null) {
1251 result = getRangeAxis(axisIndex.intValue());
1252 }
1253 return result;
1254 }
1255
1256 /**
1257 * Returns a reference to the renderer for the plot.
1258 *
1259 * @return The renderer.
1260 *
1261 * @see #setRenderer(CategoryItemRenderer)
1262 */
1263 public CategoryItemRenderer getRenderer() {
1264 return getRenderer(0);
1265 }
1266
1267 /**
1268 * Returns the renderer at the given index.
1269 *
1270 * @param index the renderer index.
1271 *
1272 * @return The renderer (possibly <code>null</code>).
1273 *
1274 * @see #setRenderer(int, CategoryItemRenderer)
1275 */
1276 public CategoryItemRenderer getRenderer(int index) {
1277 CategoryItemRenderer result = null;
1278 if (this.renderers.size() > index) {
1279 result = (CategoryItemRenderer) this.renderers.get(index);
1280 }
1281 return result;
1282 }
1283
1284 /**
1285 * Sets the renderer at index 0 (sometimes referred to as the "primary"
1286 * renderer) and sends a {@link PlotChangeEvent} to all registered
1287 * listeners.
1288 *
1289 * @param renderer the renderer (<code>null</code> permitted.
1290 *
1291 * @see #getRenderer()
1292 */
1293 public void setRenderer(CategoryItemRenderer renderer) {
1294 setRenderer(0, renderer, true);
1295 }
1296
1297 /**
1298 * Sets the renderer at index 0 (sometimes referred to as the "primary"
1299 * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
1300 * registered listeners.
1301 * <p>
1302 * You can set the renderer to <code>null</code>, but this is not
1303 * recommended because:
1304 * <ul>
1305 * <li>no data will be displayed;</li>
1306 * <li>the plot background will not be painted;</li>
1307 * </ul>
1308 *
1309 * @param renderer the renderer (<code>null</code> permitted).
1310 * @param notify notify listeners?
1311 *
1312 * @see #getRenderer()
1313 */
1314 public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
1315 setRenderer(0, renderer, notify);
1316 }
1317
1318 /**
1319 * Sets the renderer at the specified index and sends a
1320 * {@link PlotChangeEvent} to all registered listeners.
1321 *
1322 * @param index the index.
1323 * @param renderer the renderer (<code>null</code> permitted).
1324 *
1325 * @see #getRenderer(int)
1326 * @see #setRenderer(int, CategoryItemRenderer, boolean)
1327 */
1328 public void setRenderer(int index, CategoryItemRenderer renderer) {
1329 setRenderer(index, renderer, true);
1330 }
1331
1332 /**
1333 * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered
1334 * listeners.
1335 *
1336 * @param index the index.
1337 * @param renderer the renderer (<code>null</code> permitted).
1338 * @param notify notify listeners?
1339 *
1340 * @see #getRenderer(int)
1341 */
1342 public void setRenderer(int index, CategoryItemRenderer renderer,
1343 boolean notify) {
1344
1345 // stop listening to the existing renderer...
1346 CategoryItemRenderer existing
1347 = (CategoryItemRenderer) this.renderers.get(index);
1348 if (existing != null) {
1349 existing.removeChangeListener(this);
1350 }
1351
1352 // register the new renderer...
1353 this.renderers.set(index, renderer);
1354 if (renderer != null) {
1355 renderer.setPlot(this);
1356 renderer.addChangeListener(this);
1357 }
1358
1359 configureDomainAxes();
1360 configureRangeAxes();
1361
1362 if (notify) {
1363 notifyListeners(new PlotChangeEvent(this));
1364 }
1365 }
1366
1367 /**
1368 * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1369 * to all registered listeners.
1370 *
1371 * @param renderers the renderers.
1372 */
1373 public void setRenderers(CategoryItemRenderer[] renderers) {
1374 for (int i = 0; i < renderers.length; i++) {
1375 setRenderer(i, renderers[i], false);
1376 }
1377 notifyListeners(new PlotChangeEvent(this));
1378 }
1379
1380 /**
1381 * Returns the renderer for the specified dataset. If the dataset doesn't
1382 * belong to the plot, this method will return <code>null</code>.
1383 *
1384 * @param dataset the dataset (<code>null</code> permitted).
1385 *
1386 * @return The renderer (possibly <code>null</code>).
1387 */
1388 public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
1389 CategoryItemRenderer result = null;
1390 for (int i = 0; i < this.datasets.size(); i++) {
1391 if (this.datasets.get(i) == dataset) {
1392 result = (CategoryItemRenderer) this.renderers.get(i);
1393 break;
1394 }
1395 }
1396 return result;
1397 }
1398
1399 /**
1400 * Returns the index of the specified renderer, or <code>-1</code> if the
1401 * renderer is not assigned to this plot.
1402 *
1403 * @param renderer the renderer (<code>null</code> permitted).
1404 *
1405 * @return The renderer index.
1406 */
1407 public int getIndexOf(CategoryItemRenderer renderer) {
1408 return this.renderers.indexOf(renderer);
1409 }
1410
1411 /**
1412 * Returns the dataset rendering order.
1413 *
1414 * @return The order (never <code>null</code>).
1415 *
1416 * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1417 */
1418 public DatasetRenderingOrder getDatasetRenderingOrder() {
1419 return this.renderingOrder;
1420 }
1421
1422 /**
1423 * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1424 * registered listeners. By default, the plot renders the primary dataset
1425 * last (so that the primary dataset overlays the secondary datasets). You