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 * XYPlot.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): Craig MacFarlane;
034 * Mark Watson (www.markwatson.com);
035 * Jonathan Nash;
036 * Gideon Krause;
037 * Klaus Rheinwald;
038 * Xavier Poinsard;
039 * Richard Atkinson;
040 * Arnaud Lelievre;
041 * Nicolas Brodu;
042 * Eduardo Ramalho;
043 * Sergei Ivanov;
044 * Richard West, Advanced Micro Devices, Inc.;
045 * Ulrich Voigt - patch 1997549;
046 *
047 * Changes (from 21-Jun-2001)
048 * --------------------------
049 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
050 * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
051 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
052 * 19-Oct-2001 : Removed the code for drawing the visual representation of each
053 * data point into a separate class StandardXYItemRenderer.
054 * This will make it easier to add variations to the way the
055 * charts are drawn. Based on code contributed by Mark
056 * Watson (DG);
057 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
058 * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
059 * inside JScrollPane (DG);
060 * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
061 * 13-Dec-2001 : Added skeleton code for tooltips. Added new constructor. (DG);
062 * 16-Jan-2002 : Renamed the tooltips class (DG);
063 * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
064 * Crosshairs based on code by Jonathan Nash (DG);
065 * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
066 * Vieujot (DG);
067 * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
068 * special case when chart is null (DG);
069 * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
070 * 28-Mar-2002 : The plot now registers with the renderer as a property change
071 * listener. Also added a new constructor (DG);
072 * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
073 * method. Moved the tooltip generator into the renderer (DG);
074 * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
075 * lines (DG);
076 * 13-May-2002 : Small change to the draw() method so that it works for
077 * OverlaidXYPlot also (DG);
078 * 25-Jun-2002 : Removed redundant import (DG);
079 * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
080 * setXYItemRenderer() --> setRenderer() (DG);
081 * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
082 * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
083 * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
084 * these were set in the axes) (DG);
085 * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
086 * border bug fix contributed by Gideon Krause (DG);
087 * 22-Jan-2003 : Removed monolithic constructor (DG);
088 * 04-Mar-2003 : Added 'no data' message, see bug report 691634. Added
089 * secondary range markers using code contributed by Klaus
090 * Rheinwald (DG);
091 * 26-Mar-2003 : Implemented Serializable (DG);
092 * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
093 * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
094 * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
095 * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
096 * 15-May-2003 : Added an orientation attribute (DG);
097 * 02-Jun-2003 : Removed range axis compatibility test (DG);
098 * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
099 * Services Ltd) (DG);
100 * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
101 * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
102 * overlaid plots) (DG);
103 * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
104 * renderers (DG);
105 * 27-Jul-2003 : Added support for stacked XY area charts (RA);
106 * 19-Aug-2003 : Implemented Cloneable (DG);
107 * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
108 * change event (797466) (DG)
109 * 08-Sep-2003 : Added internationalization via use of properties
110 * resourceBundle (RFE 690236) (AL);
111 * 08-Sep-2003 : Changed ValueAxis API (DG);
112 * 08-Sep-2003 : Fixes for serialization (NB);
113 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
114 * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
115 * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
116 * getSecondaryRangeAxisCount() methods suggested by Eduardo
117 * Ramalho (RFE 808548) (DG);
118 * 23-Sep-2003 : Split domain and range markers into foreground and
119 * background (DG);
120 * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
121 * methods. Fixed bug (815876) in addSecondaryRangeMarker()
122 * method. Added new addSecondaryDomainMarker methods (see bug
123 * id 815869) (DG);
124 * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
125 * requested by Eduardo Ramalho (DG);
126 * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
127 * values (DG);
128 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
129 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
130 * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
131 * range type (DG);
132 * 22-Mar-2004 : Fixed cloning bug (DG);
133 * 23-Mar-2004 : Fixed more cloning bugs (DG);
134 * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
135 * stacked, see this post in the forum:
136 * http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
137 * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
138 * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
139 * plot (DG);
140 * 27-Apr-2004 : Removed major distinction between primary and secondary
141 * datasets, renderers and axes (DG);
142 * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
143 * renderer interface (DG);
144 * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
145 * 19-May-2004 : Added indexOf() method (DG);
146 * 03-Jun-2004 : Fixed zooming bug (DG);
147 * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
148 * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
149 * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
150 * the x-value range (now matches behaviour for y-values). Added
151 * getDomainAxisIndex() method (DG);
152 * 12-Nov-2004 : Implemented new Zoomable interface (DG);
153 * 25-Nov-2004 : Small update to clone() implementation (DG);
154 * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
155 * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
156 * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
157 * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
158 * 26-Apr-2005 : Removed LOGGER (DG);
159 * 04-May-2005 : Fixed serialization of domain and range markers (DG);
160 * 05-May-2005 : Removed unused draw() method (DG);
161 * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
162 * RFE 1183100 (DG);
163 * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
164 * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
165 * 01-Jun-2005 : Added clearDomainMarkers(int) method to match
166 * clearRangeMarkers(int) (DG);
167 * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
168 * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
169 * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG);
170 * ------------- JFREECHART 1.0.x ---------------------------------------------
171 * 26-Jan-2006 : Added getAnnotations() method (DG);
172 * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
173 * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report
174 * 1565168 (DG);
175 * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus
176 * API doc updates (DG);
177 * 29-Nov-2006 : Added argument checks (DG);
178 * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG);
179 * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG);
180 * 26-Feb-2007 : Added missing setDomainAxisLocation() and
181 * setRangeAxisLocation() methods (DG);
182 * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation
183 * (see patch 1671648 by Sergei Ivanov) (DG);
184 * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG);
185 * 23-Mar-2007 : Added domain zero base line facility (DG);
186 * 04-May-2007 : Render only visible data items if possible (DG);
187 * 24-May-2007 : Fixed bug in render method for an empty series (DG);
188 * 07-Jun-2007 : Modified drawBackground() to pass orientation to
189 * fillBackground() for handling GradientPaint (DG);
190 * 24-Sep-2007 : Added new zoom methods (DG);
191 * 26-Sep-2007 : Include index value in IllegalArgumentExceptions (DG);
192 * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
193 * and range markers (DG);
194 * 12-Nov-2007 : Fixed bug in equals() method for domain and range tick
195 * band paint attributes (DG);
196 * 27-Nov-2007 : Added new setFixedDomain/RangeAxisSpace() methods (DG);
197 * 04-Jan-2008 : Fix for quadrant painting error - see patch 1849564 (DG);
198 * 25-Mar-2008 : Added new methods with optional notification - see patch
199 * 1913751 (DG);
200 * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and
201 * removeRangeMarker() (DG);
202 * 22-May-2008 : Modified calculateAxisSpace() to process range axes first,
203 * then adjust the plot area before calculating the space
204 * for the domain axes (DG);
205 * 09-Jul-2008 : Added renderer state notification when series pass begins
206 * and ends - see patch 1997549 by Ulrich Voigt (DG);
207 * 25-Jul-2008 : Fixed NullPointerException for plots with no axes (DG);
208 * 15-Aug-2008 : Added getRendererCount() method (DG);
209 *
210 */
211
212 package org.jfree.chart.plot;
213
214 import java.awt.AlphaComposite;
215 import java.awt.BasicStroke;
216 import java.awt.Color;
217 import java.awt.Composite;
218 import java.awt.Graphics2D;
219 import java.awt.Paint;
220 import java.awt.Shape;
221 import java.awt.Stroke;
222 import java.awt.geom.Line2D;
223 import java.awt.geom.Point2D;
224 import java.awt.geom.Rectangle2D;
225 import java.io.IOException;
226 import java.io.ObjectInputStream;
227 import java.io.ObjectOutputStream;
228 import java.io.Serializable;
229 import java.util.ArrayList;
230 import java.util.Collection;
231 import java.util.Collections;
232 import java.util.HashMap;
233 import java.util.Iterator;
234 import java.util.List;
235 import java.util.Map;
236 import java.util.ResourceBundle;
237 import java.util.Set;
238 import java.util.TreeMap;
239
240 import org.jfree.chart.LegendItem;
241 import org.jfree.chart.LegendItemCollection;
242 import org.jfree.chart.annotations.XYAnnotation;
243 import org.jfree.chart.axis.Axis;
244 import org.jfree.chart.axis.AxisCollection;
245 import org.jfree.chart.axis.AxisLocation;
246 import org.jfree.chart.axis.AxisSpace;
247 import org.jfree.chart.axis.AxisState;
248 import org.jfree.chart.axis.ValueAxis;
249 import org.jfree.chart.axis.ValueTick;
250 import org.jfree.chart.event.ChartChangeEventType;
251 import org.jfree.chart.event.PlotChangeEvent;
252 import org.jfree.chart.event.RendererChangeEvent;
253 import org.jfree.chart.event.RendererChangeListener;
254 import org.jfree.chart.renderer.RendererUtilities;
255 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
256 import org.jfree.chart.renderer.xy.XYItemRenderer;
257 import org.jfree.chart.renderer.xy.XYItemRendererState;
258 import org.jfree.data.Range;
259 import org.jfree.data.general.Dataset;
260 import org.jfree.data.general.DatasetChangeEvent;
261 import org.jfree.data.general.DatasetUtilities;
262 import org.jfree.data.xy.XYDataset;
263 import org.jfree.io.SerialUtilities;
264 import org.jfree.ui.Layer;
265 import org.jfree.ui.RectangleEdge;
266 import org.jfree.ui.RectangleInsets;
267 import org.jfree.util.ObjectList;
268 import org.jfree.util.ObjectUtilities;
269 import org.jfree.util.PaintUtilities;
270 import org.jfree.util.PublicCloneable;
271
272 /**
273 * A general class for plotting data in the form of (x, y) pairs. This plot can
274 * use data from any class that implements the {@link XYDataset} interface.
275 * <P>
276 * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
277 * on the plot. By using different renderers, various chart types can be
278 * produced.
279 * <p>
280 * The {@link org.jfree.chart.ChartFactory} class contains static methods for
281 * creating pre-configured charts.
282 */
283 public class XYPlot extends Plot implements ValueAxisPlot, Zoomable,
284 RendererChangeListener, Cloneable, PublicCloneable, Serializable {
285
286 /** For serialization. */
287 private static final long serialVersionUID = 7044148245716569264L;
288
289 /** The default grid line stroke. */
290 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
291 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
292 new float[] {2.0f, 2.0f}, 0.0f);
293
294 /** The default grid line paint. */
295 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
296
297 /** The default crosshair visibility. */
298 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
299
300 /** The default crosshair stroke. */
301 public static final Stroke DEFAULT_CROSSHAIR_STROKE
302 = DEFAULT_GRIDLINE_STROKE;
303
304 /** The default crosshair paint. */
305 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
306
307 /** The resourceBundle for the localization. */
308 protected static ResourceBundle localizationResources
309 = ResourceBundle.getBundle(
310 "org.jfree.chart.plot.LocalizationBundle");
311
312 /** The plot orientation. */
313 private PlotOrientation orientation;
314
315 /** The offset between the data area and the axes. */
316 private RectangleInsets axisOffset;
317
318 /** The domain axis / axes (used for the x-values). */
319 private ObjectList domainAxes;
320
321 /** The domain axis locations. */
322 private ObjectList domainAxisLocations;
323
324 /** The range axis (used for the y-values). */
325 private ObjectList rangeAxes;
326
327 /** The range axis location. */
328 private ObjectList rangeAxisLocations;
329
330 /** Storage for the datasets. */
331 private ObjectList datasets;
332
333 /** Storage for the renderers. */
334 private ObjectList renderers;
335
336 /**
337 * Storage for keys that map datasets/renderers to domain axes. If the
338 * map contains no entry for a dataset, it is assumed to map to the
339 * primary domain axis (index = 0).
340 */
341 private Map datasetToDomainAxisMap;
342
343 /**
344 * Storage for keys that map datasets/renderers to range axes. If the
345 * map contains no entry for a dataset, it is assumed to map to the
346 * primary domain axis (index = 0).
347 */
348 private Map datasetToRangeAxisMap;
349
350 /** The origin point for the quadrants (if drawn). */
351 private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0);
352
353 /** The paint used for each quadrant. */
354 private transient Paint[] quadrantPaint
355 = new Paint[] {null, null, null, null};
356
357 /** A flag that controls whether the domain grid-lines are visible. */
358 private boolean domainGridlinesVisible;
359
360 /** The stroke used to draw the domain grid-lines. */
361 private transient Stroke domainGridlineStroke;
362
363 /** The paint used to draw the domain grid-lines. */
364 private transient Paint domainGridlinePaint;
365
366 /** A flag that controls whether the range grid-lines are visible. */
367 private boolean rangeGridlinesVisible;
368
369 /** The stroke used to draw the range grid-lines. */
370 private transient Stroke rangeGridlineStroke;
371
372 /** The paint used to draw the range grid-lines. */
373 private transient Paint rangeGridlinePaint;
374
375 /**
376 * A flag that controls whether or not the zero baseline against the domain
377 * axis is visible.
378 *
379 * @since 1.0.5
380 */
381 private boolean domainZeroBaselineVisible;
382
383 /**
384 * The stroke used for the zero baseline against the domain axis.
385 *
386 * @since 1.0.5
387 */
388 private transient Stroke domainZeroBaselineStroke;
389
390 /**
391 * The paint used for the zero baseline against the domain axis.
392 *
393 * @since 1.0.5
394 */
395 private transient Paint domainZeroBaselinePaint;
396
397 /**
398 * A flag that controls whether or not the zero baseline against the range
399 * axis is visible.
400 */
401 private boolean rangeZeroBaselineVisible;
402
403 /** The stroke used for the zero baseline against the range axis. */
404 private transient Stroke rangeZeroBaselineStroke;
405
406 /** The paint used for the zero baseline against the range axis. */
407 private transient Paint rangeZeroBaselinePaint;
408
409 /** A flag that controls whether or not a domain crosshair is drawn..*/
410 private boolean domainCrosshairVisible;
411
412 /** The domain crosshair value. */
413 private double domainCrosshairValue;
414
415 /** The pen/brush used to draw the crosshair (if any). */
416 private transient Stroke domainCrosshairStroke;
417
418 /** The color used to draw the crosshair (if any). */
419 private transient Paint domainCrosshairPaint;
420
421 /**
422 * A flag that controls whether or not the crosshair locks onto actual
423 * data points.
424 */
425 private boolean domainCrosshairLockedOnData = true;
426
427 /** A flag that controls whether or not a range crosshair is drawn..*/
428 private boolean rangeCrosshairVisible;
429
430 /** The range crosshair value. */
431 private double rangeCrosshairValue;
432
433 /** The pen/brush used to draw the crosshair (if any). */
434 private transient Stroke rangeCrosshairStroke;
435
436 /** The color used to draw the crosshair (if any). */
437 private transient Paint rangeCrosshairPaint;
438
439 /**
440 * A flag that controls whether or not the crosshair locks onto actual
441 * data points.
442 */
443 private boolean rangeCrosshairLockedOnData = true;
444
445 /** A map of lists of foreground markers (optional) for the domain axes. */
446 private Map foregroundDomainMarkers;
447
448 /** A map of lists of background markers (optional) for the domain axes. */
449 private Map backgroundDomainMarkers;
450
451 /** A map of lists of foreground markers (optional) for the range axes. */
452 private Map foregroundRangeMarkers;
453
454 /** A map of lists of background markers (optional) for the range axes. */
455 private Map backgroundRangeMarkers;
456
457 /**
458 * A (possibly empty) list of annotations for the plot. The list should
459 * be initialised in the constructor and never allowed to be
460 * <code>null</code>.
461 */
462 private List annotations;
463
464 /** The paint used for the domain tick bands (if any). */
465 private transient Paint domainTickBandPaint;
466
467 /** The paint used for the range tick bands (if any). */
468 private transient Paint rangeTickBandPaint;
469
470 /** The fixed domain axis space. */
471 private AxisSpace fixedDomainAxisSpace;
472
473 /** The fixed range axis space. */
474 private AxisSpace fixedRangeAxisSpace;
475
476 /**
477 * The order of the dataset rendering (REVERSE draws the primary dataset
478 * last so that it appears to be on top).
479 */
480 private DatasetRenderingOrder datasetRenderingOrder
481 = DatasetRenderingOrder.REVERSE;
482
483 /**
484 * The order of the series rendering (REVERSE draws the primary series
485 * last so that it appears to be on top).
486 */
487 private SeriesRenderingOrder seriesRenderingOrder
488 = SeriesRenderingOrder.REVERSE;
489
490 /**
491 * The weight for this plot (only relevant if this is a subplot in a
492 * combined plot).
493 */
494 private int weight;
495
496 /**
497 * An optional collection of legend items that can be returned by the
498 * getLegendItems() method.
499 */
500 private LegendItemCollection fixedLegendItems;
501
502 /**
503 * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
504 * no renderer. You should specify these items before using the plot.
505 */
506 public XYPlot() {
507 this(null, null, null, null);
508 }
509
510 /**
511 * Creates a new plot with the specified dataset, axes and renderer. Any
512 * of the arguments can be <code>null</code>, but in that case you should
513 * take care to specify the value before using the plot (otherwise a
514 * <code>NullPointerException</code> may be thrown).
515 *
516 * @param dataset the dataset (<code>null</code> permitted).
517 * @param domainAxis the domain axis (<code>null</code> permitted).
518 * @param rangeAxis the range axis (<code>null</code> permitted).
519 * @param renderer the renderer (<code>null</code> permitted).
520 */
521 public XYPlot(XYDataset dataset,
522 ValueAxis domainAxis,
523 ValueAxis rangeAxis,
524 XYItemRenderer renderer) {
525
526 super();
527
528 this.orientation = PlotOrientation.VERTICAL;
529 this.weight = 1; // only relevant when this is a subplot
530 this.axisOffset = RectangleInsets.ZERO_INSETS;
531
532 // allocate storage for datasets, axes and renderers (all optional)
533 this.domainAxes = new ObjectList();
534 this.domainAxisLocations = new ObjectList();
535 this.foregroundDomainMarkers = new HashMap();
536 this.backgroundDomainMarkers = new HashMap();
537
538 this.rangeAxes = new ObjectList();
539 this.rangeAxisLocations = new ObjectList();
540 this.foregroundRangeMarkers = new HashMap();
541 this.backgroundRangeMarkers = new HashMap();
542
543 this.datasets = new ObjectList();
544 this.renderers = new ObjectList();
545
546 this.datasetToDomainAxisMap = new TreeMap();
547 this.datasetToRangeAxisMap = new TreeMap();
548
549 this.datasets.set(0, dataset);
550 if (dataset != null) {
551 dataset.addChangeListener(this);
552 }
553
554 this.renderers.set(0, renderer);
555 if (renderer != null) {
556 renderer.setPlot(this);
557 renderer.addChangeListener(this);
558 }
559
560 this.domainAxes.set(0, domainAxis);
561 this.mapDatasetToDomainAxis(0, 0);
562 if (domainAxis != null) {
563 domainAxis.setPlot(this);
564 domainAxis.addChangeListener(this);
565 }
566 this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
567
568 this.rangeAxes.set(0, rangeAxis);
569 this.mapDatasetToRangeAxis(0, 0);
570 if (rangeAxis != null) {
571 rangeAxis.setPlot(this);
572 rangeAxis.addChangeListener(this);
573 }
574 this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
575
576 configureDomainAxes();
577 configureRangeAxes();
578
579 this.domainGridlinesVisible = true;
580 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
581 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
582
583 this.domainZeroBaselineVisible = false;
584 this.domainZeroBaselinePaint = Color.black;
585 this.domainZeroBaselineStroke = new BasicStroke(0.5f);
586
587 this.rangeGridlinesVisible = true;
588 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
589 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
590
591 this.rangeZeroBaselineVisible = false;
592 this.rangeZeroBaselinePaint = Color.black;
593 this.rangeZeroBaselineStroke = new BasicStroke(0.5f);
594
595 this.domainCrosshairVisible = false;
596 this.domainCrosshairValue = 0.0;
597 this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
598 this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
599
600 this.rangeCrosshairVisible = false;
601 this.rangeCrosshairValue = 0.0;
602 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
603 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
604
605 this.annotations = new java.util.ArrayList();
606
607 }
608
609 /**
610 * Returns the plot type as a string.
611 *
612 * @return A short string describing the type of plot.
613 */
614 public String getPlotType() {
615 return localizationResources.getString("XY_Plot");
616 }
617
618 /**
619 * Returns the orientation of the plot.
620 *
621 * @return The orientation (never <code>null</code>).
622 *
623 * @see #setOrientation(PlotOrientation)
624 */
625 public PlotOrientation getOrientation() {
626 return this.orientation;
627 }
628
629 /**
630 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
631 * all registered listeners.
632 *
633 * @param orientation the orientation (<code>null</code> not allowed).
634 *
635 * @see #getOrientation()
636 */
637 public void setOrientation(PlotOrientation orientation) {
638 if (orientation == null) {
639 throw new IllegalArgumentException("Null 'orientation' argument.");
640 }
641 if (orientation != this.orientation) {
642 this.orientation = orientation;
643 fireChangeEvent();
644 }
645 }
646
647 /**
648 * Returns the axis offset.
649 *
650 * @return The axis offset (never <code>null</code>).
651 *
652 * @see #setAxisOffset(RectangleInsets)
653 */
654 public RectangleInsets getAxisOffset() {
655 return this.axisOffset;
656 }
657
658 /**
659 * Sets the axis offsets (gap between the data area and the axes) and sends
660 * a {@link PlotChangeEvent} to all registered listeners.
661 *
662 * @param offset the offset (<code>null</code> not permitted).
663 *
664 * @see #getAxisOffset()
665 */
666 public void setAxisOffset(RectangleInsets offset) {
667 if (offset == null) {
668 throw new IllegalArgumentException("Null 'offset' argument.");
669 }
670 this.axisOffset = offset;
671 fireChangeEvent();
672 }
673
674 /**
675 * Returns the domain axis with index 0. If the domain axis for this plot
676 * is <code>null</code>, then the method will return the parent plot's
677 * domain axis (if there is a parent plot).
678 *
679 * @return The domain axis (possibly <code>null</code>).
680 *
681 * @see #getDomainAxis(int)
682 * @see #setDomainAxis(ValueAxis)
683 */
684 public ValueAxis getDomainAxis() {
685 return getDomainAxis(0);
686 }
687
688 /**
689 * Returns the domain axis with the specified index, or <code>null</code>.
690 *
691 * @param index the axis index.
692 *
693 * @return The axis (<code>null</code> possible).
694 *
695 * @see #setDomainAxis(int, ValueAxis)
696 */
697 public ValueAxis getDomainAxis(int index) {
698 ValueAxis result = null;
699 if (index < this.domainAxes.size()) {
700 result = (ValueAxis) this.domainAxes.get(index);
701 }
702 if (result == null) {
703 Plot parent = getParent();
704 if (parent instanceof XYPlot) {
705 XYPlot xy = (XYPlot) parent;
706 result = xy.getDomainAxis(index);
707 }
708 }
709 return result;
710 }
711
712 /**
713 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
714 * to all registered listeners.
715 *
716 * @param axis the new axis (<code>null</code> permitted).
717 *
718 * @see #getDomainAxis()
719 * @see #setDomainAxis(int, ValueAxis)
720 */
721 public void setDomainAxis(ValueAxis axis) {
722 setDomainAxis(0, axis);
723 }
724
725 /**
726 * Sets a domain axis and sends a {@link PlotChangeEvent} to all
727 * registered listeners.
728 *
729 * @param index the axis index.
730 * @param axis the axis (<code>null</code> permitted).
731 *
732 * @see #getDomainAxis(int)
733 * @see #setRangeAxis(int, ValueAxis)
734 */
735 public void setDomainAxis(int index, ValueAxis axis) {
736 setDomainAxis(index, axis, true);
737 }
738
739 /**
740 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
741 * all registered listeners.
742 *
743 * @param index the axis index.
744 * @param axis the axis.
745 * @param notify notify listeners?
746 *
747 * @see #getDomainAxis(int)
748 */
749 public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
750 ValueAxis existing = getDomainAxis(index);
751 if (existing != null) {
752 existing.removeChangeListener(this);
753 }
754 if (axis != null) {
755 axis.setPlot(this);
756 }
757 this.domainAxes.set(index, axis);
758 if (axis != null) {
759 axis.configure();
760 axis.addChangeListener(this);
761 }
762 if (notify) {
763 fireChangeEvent();
764 }
765 }
766
767 /**
768 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
769 * to all registered listeners.
770 *
771 * @param axes the axes (<code>null</code> not permitted).
772 *
773 * @see #setRangeAxes(ValueAxis[])
774 */
775 public void setDomainAxes(ValueAxis[] axes) {
776 for (int i = 0; i < axes.length; i++) {
777 setDomainAxis(i, axes[i], false);
778 }
779 fireChangeEvent();
780 }
781
782 /**
783 * Returns the location of the primary domain axis.
784 *
785 * @return The location (never <code>null</code>).
786 *
787 * @see #setDomainAxisLocation(AxisLocation)
788 */
789 public AxisLocation getDomainAxisLocation() {
790 return (AxisLocation) this.domainAxisLocations.get(0);
791 }
792
793 /**
794 * Sets the location of the primary domain axis and sends a
795 * {@link PlotChangeEvent} to all registered listeners.
796 *
797 * @param location the location (<code>null</code> not permitted).
798 *
799 * @see #getDomainAxisLocation()
800 */
801 public void setDomainAxisLocation(AxisLocation location) {
802 // delegate...
803 setDomainAxisLocation(0, location, true);
804 }
805
806 /**
807 * Sets the location of the domain axis and, if requested, sends a
808 * {@link PlotChangeEvent} to all registered listeners.
809 *
810 * @param location the location (<code>null</code> not permitted).
811 * @param notify notify listeners?
812 *
813 * @see #getDomainAxisLocation()
814 */
815 public void setDomainAxisLocation(AxisLocation location, boolean notify) {
816 // delegate...
817 setDomainAxisLocation(0, location, notify);
818 }
819
820 /**
821 * Returns the edge for the primary domain axis (taking into account the
822 * plot's orientation).
823 *
824 * @return The edge.
825 *
826 * @see #getDomainAxisLocation()
827 * @see #getOrientation()
828 */
829 public RectangleEdge getDomainAxisEdge() {
830 return Plot.resolveDomainAxisLocation(getDomainAxisLocation(),
831 this.orientation);
832 }
833
834 /**
835 * Returns the number of domain axes.
836 *
837 * @return The axis count.
838 *
839 * @see #getRangeAxisCount()
840 */
841 public int getDomainAxisCount() {
842 return this.domainAxes.size();
843 }
844
845 /**
846 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
847 * to all registered listeners.
848 *
849 * @see #clearRangeAxes()
850 */
851 public void clearDomainAxes() {
852 for (int i = 0; i < this.domainAxes.size(); i++) {
853 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
854 if (axis != null) {
855 axis.removeChangeListener(this);
856 }
857 }
858 this.domainAxes.clear();
859 fireChangeEvent();
860 }
861
862 /**
863 * Configures the domain axes.
864 */
865 public void configureDomainAxes() {
866 for (int i = 0; i < this.domainAxes.size(); i++) {
867 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
868 if (axis != null) {
869 axis.configure();
870 }
871 }
872 }
873
874 /**
875 * Returns the location for a domain axis. If this hasn't been set
876 * explicitly, the method returns the location that is opposite to the
877 * primary domain axis location.
878 *
879 * @param index the axis index.
880 *
881 * @return The location (never <code>null</code>).
882 *
883 * @see #setDomainAxisLocation(int, AxisLocation)
884 */
885 public AxisLocation getDomainAxisLocation(int index) {
886 AxisLocation result = null;
887 if (index < this.domainAxisLocations.size()) {
888 result = (AxisLocation) this.domainAxisLocations.get(index);
889 }
890 if (result == null) {
891 result = AxisLocation.getOpposite(getDomainAxisLocation());
892 }
893 return result;
894 }
895
896 /**
897 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
898 * to all registered listeners.
899 *
900 * @param index the axis index.
901 * @param location the location (<code>null</code> not permitted for index
902 * 0).
903 *
904 * @see #getDomainAxisLocation(int)
905 */
906 public void setDomainAxisLocation(int index, AxisLocation location) {
907 // delegate...
908 setDomainAxisLocation(index, location, true);
909 }
910
911 /**
912 * Sets the axis location for a domain axis and, if requested, sends a
913 * {@link PlotChangeEvent} to all registered listeners.
914 *
915 * @param index the axis index.
916 * @param location the location (<code>null</code> not permitted for
917 * index 0).
918 * @param notify notify listeners?
919 *
920 * @since 1.0.5
921 *
922 * @see #getDomainAxisLocation(int)
923 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
924 */
925 public void setDomainAxisLocation(int index, AxisLocation location,
926 boolean notify) {
927
928 if (index == 0 && location == null) {
929 throw new IllegalArgumentException(
930 "Null 'location' for index 0 not permitted.");
931 }
932 this.domainAxisLocations.set(index, location);
933 if (notify) {
934 fireChangeEvent();
935 }
936 }
937
938 /**
939 * Returns the edge for a domain axis.
940 *
941 * @param index the axis index.
942 *
943 * @return The edge.
944 *
945 * @see #getRangeAxisEdge(int)
946 */
947 public RectangleEdge getDomainAxisEdge(int index) {
948 AxisLocation location = getDomainAxisLocation(index);
949 RectangleEdge result = Plot.resolveDomainAxisLocation(location,
950 this.orientation);
951 if (result == null) {
952 result = RectangleEdge.opposite(getDomainAxisEdge());
953 }
954 return result;
955 }
956
957 /**
958 * Returns the range axis for the plot. If the range axis for this plot is
959 * <code>null</code>, then the method will return the parent plot's range
960 * axis (if there is a parent plot).
961 *
962 * @return The range axis.
963 *
964 * @see #getRangeAxis(int)
965 * @see #setRangeAxis(ValueAxis)
966 */
967 public ValueAxis getRangeAxis() {
968 return getRangeAxis(0);
969 }
970
971 /**
972 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
973 * all registered listeners.
974 *
975 * @param axis the axis (<code>null</code> permitted).
976 *
977 * @see #getRangeAxis()
978 * @see #setRangeAxis(int, ValueAxis)
979 */
980 public void setRangeAxis(ValueAxis axis) {
981
982 if (axis != null) {
983 axis.setPlot(this);
984 }
985
986 // plot is likely registered as a listener with the existing axis...
987 ValueAxis existing = getRangeAxis();
988 if (existing != null) {
989 existing.removeChangeListener(this);
990 }
991
992 this.rangeAxes.set(0, axis);
993 if (axis != null) {
994 axis.configure();
995 axis.addChangeListener(this);
996 }
997 fireChangeEvent();
998
999 }
1000
1001 /**
1002 * Returns the location of the primary range axis.
1003 *
1004 * @return The location (never <code>null</code>).
1005 *
1006 * @see #setRangeAxisLocation(AxisLocation)
1007 */
1008 public AxisLocation getRangeAxisLocation() {
1009 return (AxisLocation) this.rangeAxisLocations.get(0);
1010 }
1011
1012 /**
1013 * Sets the location of the primary range axis and sends a
1014 * {@link PlotChangeEvent} to all registered listeners.
1015 *
1016 * @param location the location (<code>null</code> not permitted).
1017 *
1018 * @see #getRangeAxisLocation()
1019 */
1020 public void setRangeAxisLocation(AxisLocation location) {
1021 // delegate...
1022 setRangeAxisLocation(0, location, true);
1023 }
1024
1025 /**
1026 * Sets the location of the primary range axis and, if requested, sends a
1027 * {@link PlotChangeEvent} to all registered listeners.
1028 *
1029 * @param location the location (<code>null</code> not permitted).
1030 * @param notify notify listeners?
1031 *
1032 * @see #getRangeAxisLocation()
1033 */
1034 public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1035 // delegate...
1036 setRangeAxisLocation(0, location, notify);
1037 }
1038
1039 /**
1040 * Returns the edge for the primary range axis.
1041 *
1042 * @return The range axis edge.
1043 *
1044 * @see #getRangeAxisLocation()
1045 * @see #getOrientation()
1046 */
1047 public RectangleEdge getRangeAxisEdge() {
1048 return Plot.resolveRangeAxisLocation(getRangeAxisLocation(),
1049 this.orientation);
1050 }
1051
1052 /**
1053 * Returns a range axis.
1054 *
1055 * @param index the axis index.
1056 *
1057 * @return The axis (<code>null</code> possible).
1058 *
1059 * @see #setRangeAxis(int, ValueAxis)
1060 */
1061 public ValueAxis getRangeAxis(int index) {
1062 ValueAxis result = null;
1063 if (index < this.rangeAxes.size()) {
1064 result = (ValueAxis) this.rangeAxes.get(index);
1065 }
1066 if (result == null) {
1067 Plot parent = getParent();
1068 if (parent instanceof XYPlot) {
1069 XYPlot xy = (XYPlot) parent;
1070 result = xy.getRangeAxis(index);
1071 }
1072 }
1073 return result;
1074 }
1075
1076 /**
1077 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1078 * listeners.
1079 *
1080 * @param index the axis index.
1081 * @param axis the axis (<code>null</code> permitted).
1082 *
1083 * @see #getRangeAxis(int)
1084 */
1085 public void setRangeAxis(int index, ValueAxis axis) {
1086 setRangeAxis(index, axis, true);
1087 }
1088
1089 /**
1090 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
1091 * all registered listeners.
1092 *
1093 * @param index the axis index.
1094 * @param axis the axis (<code>null</code> permitted).
1095 * @param notify notify listeners?
1096 *
1097 * @see #getRangeAxis(int)
1098 */
1099 public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1100 ValueAxis existing = getRangeAxis(index);
1101 if (existing != null) {
1102 existing.removeChangeListener(this);
1103 }
1104 if (axis != null) {
1105 axis.setPlot(this);
1106 }
1107 this.rangeAxes.set(index, axis);
1108 if (axis != null) {
1109 axis.configure();
1110 axis.addChangeListener(this);
1111 }
1112 if (notify) {
1113 fireChangeEvent();
1114 }
1115 }
1116
1117 /**
1118 * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1119 * to all registered listeners.
1120 *
1121 * @param axes the axes (<code>null</code> not permitted).
1122 *
1123 * @see #setDomainAxes(ValueAxis[])
1124 */
1125 public void setRangeAxes(ValueAxis[] axes) {
1126 for (int i = 0; i < axes.length; i++) {
1127 setRangeAxis(i, axes[i], false);
1128 }
1129 fireChangeEvent();
1130 }
1131
1132 /**
1133 * Returns the number of range axes.
1134 *
1135 * @return The axis count.
1136 *
1137 * @see #getDomainAxisCount()
1138 */
1139 public int getRangeAxisCount() {
1140 return this.rangeAxes.size();
1141 }
1142
1143 /**
1144 * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1145 * to all registered listeners.
1146 *
1147 * @see #clearDomainAxes()
1148 */
1149 public void clearRangeAxes() {
1150 for (int i = 0; i < this.rangeAxes.size(); i++) {
1151 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1152 if (axis != null) {
1153 axis.removeChangeListener(this);
1154 }
1155 }
1156 this.rangeAxes.clear();
1157 fireChangeEvent();
1158 }
1159
1160 /**
1161 * Configures the range axes.
1162 *
1163 * @see #configureDomainAxes()
1164 */
1165 public void configureRangeAxes() {
1166 for (int i = 0; i < this.rangeAxes.size(); i++) {
1167 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1168 if (axis != null) {
1169 axis.configure();
1170 }
1171 }
1172 }
1173
1174 /**
1175 * Returns the location for a range axis. If this hasn't been set
1176 * explicitly, the method returns the location that is opposite to the
1177 * primary range axis location.
1178 *
1179 * @param index the axis index.
1180 *
1181 * @return The location (never <code>null</code>).
1182 *
1183 * @see #setRangeAxisLocation(int, AxisLocation)
1184 */
1185 public AxisLocation getRangeAxisLocation(int index) {
1186 AxisLocation result = null;
1187 if (index < this.rangeAxisLocations.size()) {
1188 result = (AxisLocation) this.rangeAxisLocations.get(index);
1189 }
1190 if (result == null) {
1191 result = AxisLocation.getOpposite(getRangeAxisLocation());
1192 }
1193 return result;
1194 }
1195
1196 /**
1197 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1198 * to all registered listeners.
1199 *
1200 * @param index the axis index.
1201 * @param location the location (<code>null</code> permitted).
1202 *
1203 * @see #getRangeAxisLocation(int)
1204 */
1205 public void setRangeAxisLocation(int index, AxisLocation location) {
1206 // delegate...
1207 setRangeAxisLocation(index, location, true);
1208 }
1209
1210 /**
1211 * Sets the axis location for a domain axis and, if requested, sends a
1212 * {@link PlotChangeEvent} to all registered listeners.
1213 *
1214 * @param index the axis index.
1215 * @param location the location (<code>null</code> not permitted for
1216 * index 0).
1217 * @param notify notify listeners?
1218 *
1219 * @since 1.0.5
1220 *
1221 * @see #getRangeAxisLocation(int)
1222 * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1223 */
1224 public void setRangeAxisLocation(int index, AxisLocation location,
1225 boolean notify) {
1226
1227 if (index == 0 && location == null) {
1228 throw new IllegalArgumentException(
1229 "Null 'location' for index 0 not permitted.");
1230 }
1231 this.rangeAxisLocations.set(index, location);
1232 if (notify) {
1233 fireChangeEvent();
1234 }
1235 }
1236
1237 /**
1238 * Returns the edge for a range axis.
1239 *
1240 * @param index the axis index.
1241 *
1242 * @return The edge.
1243 *
1244 * @see #getRangeAxisLocation(int)
1245 * @see #getOrientation()
1246 */
1247 public RectangleEdge getRangeAxisEdge(int index) {
1248 AxisLocation location = getRangeAxisLocation(index);
1249 RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1250 this.orientation);
1251 if (result == null) {
1252 result = RectangleEdge.opposite(getRangeAxisEdge());
1253 }
1254 return result;
1255 }
1256
1257 /**
1258 * Returns the primary dataset for the plot.
1259 *
1260 * @return The primary dataset (possibly <code>null</code>).
1261 *
1262 * @see #getDataset(int)
1263 * @see #setDataset(XYDataset)
1264 */
1265 public XYDataset getDataset() {
1266 return getDataset(0);
1267 }
1268
1269 /**
1270 * Returns a dataset.
1271 *
1272 * @param index the dataset index.
1273 *
1274 * @return The dataset (possibly <code>null</code>).
1275 *
1276 * @see #setDataset(int, XYDataset)
1277 */
1278 public XYDataset getDataset(int index) {
1279 XYDataset result = null;
1280 if (this.datasets.size() > index) {
1281 result = (XYDataset) this.datasets.get(index);
1282 }
1283 return result;
1284 }
1285
1286 /**
1287 * Sets the primary dataset for the plot, replacing the existing dataset if
1288 * there is one.
1289 *
1290 * @param dataset the dataset (<code>null</code> permitted).
1291 *
1292 * @see #getDataset()
1293 * @see #setDataset(int, XYDataset)
1294 */
1295 public void setDataset(XYDataset dataset) {
1296 setDataset(0, dataset);
1297 }
1298
1299 /**
1300 * Sets a dataset for the plot.
1301 *
1302 * @param index the dataset index.
1303 * @param dataset the dataset (<code>null</code> permitted).
1304 *
1305 * @see #getDataset(int)
1306 */
1307 public void setDataset(int index, XYDataset dataset) {
1308 XYDataset existing = getDataset(index);
1309 if (existing != null) {
1310 existing.removeChangeListener(this);
1311 }
1312 this.datasets.set(index, dataset);
1313 if (dataset != null) {
1314 dataset.addChangeListener(this);
1315 }
1316
1317 // send a dataset change event to self...
1318 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1319 datasetChanged(event);
1320 }
1321
1322 /**
1323 * Returns the number of datasets.
1324 *
1325 * @return The number of datasets.
1326 */
1327 public int getDatasetCount() {
1328 return this.datasets.size();
1329 }
1330
1331 /**
1332 * Returns the index of the specified dataset, or <code>-1</code> if the
1333 * dataset does not belong to the plot.
1334 *
1335 * @param dataset the dataset (<code>null</code> not permitted).
1336 *
1337 * @return The index.
1338 */
1339 public int indexOf(XYDataset dataset) {
1340 int result = -1;
1341 for (int i = 0; i < this.datasets.size(); i++) {
1342 if (dataset == this.datasets.get(i)) {
1343 result = i;
1344 break;
1345 }
1346 }
1347 return result;
1348 }
1349
1350 /**
1351 * Maps a dataset to a particular domain axis. All data will be plotted
1352 * against axis zero by default, no mapping is required for this case.
1353 *
1354 * @param index the dataset index (zero-based).
1355 * @param axisIndex the axis index.
1356 *
1357 * @see #mapDatasetToRangeAxis(int, int)
1358 */
1359 public void mapDatasetToDomainAxis(int index, int axisIndex) {
1360 this.datasetToDomainAxisMap.put(new Integer(index),
1361 new Integer(axisIndex));
1362 // fake a dataset change event to update axes...
1363 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1364 }
1365
1366 /**
1367 * Maps a dataset to a particular range axis. All data will be plotted
1368 * against axis zero by default, no mapping is required for this case.
1369 *
1370 * @param index the dataset index (zero-based).
1371 * @param axisIndex the axis index.
1372 *
1373 * @see #mapDatasetToDomainAxis(int, int)
1374 */
1375 public void mapDatasetToRangeAxis(int index, int axisIndex) {
1376 this.datasetToRangeAxisMap.put(new Integer(index),
1377 new Integer(axisIndex));
1378 // fake a dataset change event to update axes...
1379 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1380 }
1381
1382 /**
1383 * Returns the number of renderer slots for this plot.
1384 *
1385 * @return The number of renderer slots.
1386 *
1387 * @since 1.0.11
1388 */
1389 public int getRendererCount() {
1390 return this.renderers.size();
1391 }
1392
1393 /**
1394 * Returns the renderer for the primary dataset.
1395 *
1396 * @return The item renderer (possibly <code>null</code>).
1397 *
1398 * @see #setRenderer(XYItemRenderer)
1399 */
1400 public XYItemRenderer getRenderer() {
1401 return getRenderer(0);
1402 }
1403
1404 /**
1405 * Returns the renderer for a dataset, or <code>null</code>.
1406 *
1407 * @param index the renderer index.
1408 *
1409 * @return The renderer (possibly <code>null</code>).
1410 *
1411 * @see #setRenderer(int, XYItemRenderer)
1412 */
1413 pu