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 * XYPlot.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): 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 *
045 * $Id: XYPlot.java,v 1.44.2.29 2007/06/07 12:49:36 mungady Exp $
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 *
191 */
192
193 package org.jfree.chart.plot;
194
195 import java.awt.AlphaComposite;
196 import java.awt.BasicStroke;
197 import java.awt.Color;
198 import java.awt.Composite;
199 import java.awt.Graphics2D;
200 import java.awt.Paint;
201 import java.awt.Shape;
202 import java.awt.Stroke;
203 import java.awt.geom.Line2D;
204 import java.awt.geom.Point2D;
205 import java.awt.geom.Rectangle2D;
206 import java.io.IOException;
207 import java.io.ObjectInputStream;
208 import java.io.ObjectOutputStream;
209 import java.io.Serializable;
210 import java.util.ArrayList;
211 import java.util.Collection;
212 import java.util.Collections;
213 import java.util.HashMap;
214 import java.util.Iterator;
215 import java.util.List;
216 import java.util.Map;
217 import java.util.ResourceBundle;
218 import java.util.Set;
219 import java.util.TreeMap;
220
221 import org.jfree.chart.LegendItem;
222 import org.jfree.chart.LegendItemCollection;
223 import org.jfree.chart.annotations.XYAnnotation;
224 import org.jfree.chart.axis.Axis;
225 import org.jfree.chart.axis.AxisCollection;
226 import org.jfree.chart.axis.AxisLocation;
227 import org.jfree.chart.axis.AxisSpace;
228 import org.jfree.chart.axis.AxisState;
229 import org.jfree.chart.axis.ValueAxis;
230 import org.jfree.chart.axis.ValueTick;
231 import org.jfree.chart.event.ChartChangeEventType;
232 import org.jfree.chart.event.PlotChangeEvent;
233 import org.jfree.chart.event.RendererChangeEvent;
234 import org.jfree.chart.event.RendererChangeListener;
235 import org.jfree.chart.renderer.RendererUtilities;
236 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
237 import org.jfree.chart.renderer.xy.XYItemRenderer;
238 import org.jfree.chart.renderer.xy.XYItemRendererState;
239 import org.jfree.data.Range;
240 import org.jfree.data.general.Dataset;
241 import org.jfree.data.general.DatasetChangeEvent;
242 import org.jfree.data.general.DatasetUtilities;
243 import org.jfree.data.xy.XYDataset;
244 import org.jfree.io.SerialUtilities;
245 import org.jfree.ui.Layer;
246 import org.jfree.ui.RectangleEdge;
247 import org.jfree.ui.RectangleInsets;
248 import org.jfree.util.ObjectList;
249 import org.jfree.util.ObjectUtilities;
250 import org.jfree.util.PaintUtilities;
251 import org.jfree.util.PublicCloneable;
252
253 /**
254 * A general class for plotting data in the form of (x, y) pairs. This plot can
255 * use data from any class that implements the {@link XYDataset} interface.
256 * <P>
257 * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
258 * on the plot. By using different renderers, various chart types can be
259 * produced.
260 * <p>
261 * The {@link org.jfree.chart.ChartFactory} class contains static methods for
262 * creating pre-configured charts.
263 */
264 public class XYPlot extends Plot implements ValueAxisPlot,
265 Zoomable,
266 RendererChangeListener,
267 Cloneable, PublicCloneable,
268 Serializable {
269
270 /** For serialization. */
271 private static final long serialVersionUID = 7044148245716569264L;
272
273 /** The default grid line stroke. */
274 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
275 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
276 new float[] {2.0f, 2.0f}, 0.0f);
277
278 /** The default grid line paint. */
279 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
280
281 /** The default crosshair visibility. */
282 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
283
284 /** The default crosshair stroke. */
285 public static final Stroke DEFAULT_CROSSHAIR_STROKE
286 = DEFAULT_GRIDLINE_STROKE;
287
288 /** The default crosshair paint. */
289 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
290
291 /** The resourceBundle for the localization. */
292 protected static ResourceBundle localizationResources
293 = ResourceBundle.getBundle(
294 "org.jfree.chart.plot.LocalizationBundle");
295
296 /** The plot orientation. */
297 private PlotOrientation orientation;
298
299 /** The offset between the data area and the axes. */
300 private RectangleInsets axisOffset;
301
302 /** The domain axis / axes (used for the x-values). */
303 private ObjectList domainAxes;
304
305 /** The domain axis locations. */
306 private ObjectList domainAxisLocations;
307
308 /** The range axis (used for the y-values). */
309 private ObjectList rangeAxes;
310
311 /** The range axis location. */
312 private ObjectList rangeAxisLocations;
313
314 /** Storage for the datasets. */
315 private ObjectList datasets;
316
317 /** Storage for the renderers. */
318 private ObjectList renderers;
319
320 /**
321 * Storage for keys that map datasets/renderers to domain axes. If the
322 * map contains no entry for a dataset, it is assumed to map to the
323 * primary domain axis (index = 0).
324 */
325 private Map datasetToDomainAxisMap;
326
327 /**
328 * Storage for keys that map datasets/renderers to range axes. If the
329 * map contains no entry for a dataset, it is assumed to map to the
330 * primary domain axis (index = 0).
331 */
332 private Map datasetToRangeAxisMap;
333
334 /** The origin point for the quadrants (if drawn). */
335 private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0);
336
337 /** The paint used for each quadrant. */
338 private transient Paint[] quadrantPaint
339 = new Paint[] {null, null, null, null};
340
341 /** A flag that controls whether the domain grid-lines are visible. */
342 private boolean domainGridlinesVisible;
343
344 /** The stroke used to draw the domain grid-lines. */
345 private transient Stroke domainGridlineStroke;
346
347 /** The paint used to draw the domain grid-lines. */
348 private transient Paint domainGridlinePaint;
349
350 /** A flag that controls whether the range grid-lines are visible. */
351 private boolean rangeGridlinesVisible;
352
353 /** The stroke used to draw the range grid-lines. */
354 private transient Stroke rangeGridlineStroke;
355
356 /** The paint used to draw the range grid-lines. */
357 private transient Paint rangeGridlinePaint;
358
359 /**
360 * A flag that controls whether or not the zero baseline against the domain
361 * axis is visible.
362 *
363 * @since 1.0.5
364 */
365 private boolean domainZeroBaselineVisible;
366
367 /**
368 * The stroke used for the zero baseline against the domain axis.
369 *
370 * @since 1.0.5
371 */
372 private transient Stroke domainZeroBaselineStroke;
373
374 /**
375 * The paint used for the zero baseline against the domain axis.
376 *
377 * @since 1.0.5
378 */
379 private transient Paint domainZeroBaselinePaint;
380
381 /**
382 * A flag that controls whether or not the zero baseline against the range
383 * axis is visible.
384 */
385 private boolean rangeZeroBaselineVisible;
386
387 /** The stroke used for the zero baseline against the range axis. */
388 private transient Stroke rangeZeroBaselineStroke;
389
390 /** The paint used for the zero baseline against the range axis. */
391 private transient Paint rangeZeroBaselinePaint;
392
393 /** A flag that controls whether or not a domain crosshair is drawn..*/
394 private boolean domainCrosshairVisible;
395
396 /** The domain crosshair value. */
397 private double domainCrosshairValue;
398
399 /** The pen/brush used to draw the crosshair (if any). */
400 private transient Stroke domainCrosshairStroke;
401
402 /** The color used to draw the crosshair (if any). */
403 private transient Paint domainCrosshairPaint;
404
405 /**
406 * A flag that controls whether or not the crosshair locks onto actual
407 * data points.
408 */
409 private boolean domainCrosshairLockedOnData = true;
410
411 /** A flag that controls whether or not a range crosshair is drawn..*/
412 private boolean rangeCrosshairVisible;
413
414 /** The range crosshair value. */
415 private double rangeCrosshairValue;
416
417 /** The pen/brush used to draw the crosshair (if any). */
418 private transient Stroke rangeCrosshairStroke;
419
420 /** The color used to draw the crosshair (if any). */
421 private transient Paint rangeCrosshairPaint;
422
423 /**
424 * A flag that controls whether or not the crosshair locks onto actual
425 * data points.
426 */
427 private boolean rangeCrosshairLockedOnData = true;
428
429 /** A map of lists of foreground markers (optional) for the domain axes. */
430 private Map foregroundDomainMarkers;
431
432 /** A map of lists of background markers (optional) for the domain axes. */
433 private Map backgroundDomainMarkers;
434
435 /** A map of lists of foreground markers (optional) for the range axes. */
436 private Map foregroundRangeMarkers;
437
438 /** A map of lists of background markers (optional) for the range axes. */
439 private Map backgroundRangeMarkers;
440
441 /**
442 * A (possibly empty) list of annotations for the plot. The list should
443 * be initialised in the constructor and never allowed to be
444 * <code>null</code>.
445 */
446 private List annotations;
447
448 /** The paint used for the domain tick bands (if any). */
449 private transient Paint domainTickBandPaint;
450
451 /** The paint used for the range tick bands (if any). */
452 private transient Paint rangeTickBandPaint;
453
454 /** The fixed domain axis space. */
455 private AxisSpace fixedDomainAxisSpace;
456
457 /** The fixed range axis space. */
458 private AxisSpace fixedRangeAxisSpace;
459
460 /**
461 * The order of the dataset rendering (REVERSE draws the primary dataset
462 * last so that it appears to be on top).
463 */
464 private DatasetRenderingOrder datasetRenderingOrder
465 = DatasetRenderingOrder.REVERSE;
466
467 /**
468 * The order of the series rendering (REVERSE draws the primary series
469 * last so that it appears to be on top).
470 */
471 private SeriesRenderingOrder seriesRenderingOrder
472 = SeriesRenderingOrder.REVERSE;
473
474 /**
475 * The weight for this plot (only relevant if this is a subplot in a
476 * combined plot).
477 */
478 private int weight;
479
480 /**
481 * An optional collection of legend items that can be returned by the
482 * getLegendItems() method.
483 */
484 private LegendItemCollection fixedLegendItems;
485
486 /**
487 * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
488 * no renderer. You should specify these items before using the plot.
489 */
490 public XYPlot() {
491 this(null, null, null, null);
492 }
493
494 /**
495 * Creates a new plot with the specified dataset, axes and renderer. Any
496 * of the arguments can be <code>null</code>, but in that case you should
497 * take care to specify the value before using the plot (otherwise a
498 * <code>NullPointerException</code> may be thrown).
499 *
500 * @param dataset the dataset (<code>null</code> permitted).
501 * @param domainAxis the domain axis (<code>null</code> permitted).
502 * @param rangeAxis the range axis (<code>null</code> permitted).
503 * @param renderer the renderer (<code>null</code> permitted).
504 */
505 public XYPlot(XYDataset dataset,
506 ValueAxis domainAxis,
507 ValueAxis rangeAxis,
508 XYItemRenderer renderer) {
509
510 super();
511
512 this.orientation = PlotOrientation.VERTICAL;
513 this.weight = 1; // only relevant when this is a subplot
514 this.axisOffset = RectangleInsets.ZERO_INSETS;
515
516 // allocate storage for datasets, axes and renderers (all optional)
517 this.domainAxes = new ObjectList();
518 this.domainAxisLocations = new ObjectList();
519 this.foregroundDomainMarkers = new HashMap();
520 this.backgroundDomainMarkers = new HashMap();
521
522 this.rangeAxes = new ObjectList();
523 this.rangeAxisLocations = new ObjectList();
524 this.foregroundRangeMarkers = new HashMap();
525 this.backgroundRangeMarkers = new HashMap();
526
527 this.datasets = new ObjectList();
528 this.renderers = new ObjectList();
529
530 this.datasetToDomainAxisMap = new TreeMap();
531 this.datasetToRangeAxisMap = new TreeMap();
532
533 this.datasets.set(0, dataset);
534 if (dataset != null) {
535 dataset.addChangeListener(this);
536 }
537
538 this.renderers.set(0, renderer);
539 if (renderer != null) {
540 renderer.setPlot(this);
541 renderer.addChangeListener(this);
542 }
543
544 this.domainAxes.set(0, domainAxis);
545 this.mapDatasetToDomainAxis(0, 0);
546 if (domainAxis != null) {
547 domainAxis.setPlot(this);
548 domainAxis.addChangeListener(this);
549 }
550 this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
551
552 this.rangeAxes.set(0, rangeAxis);
553 this.mapDatasetToRangeAxis(0, 0);
554 if (rangeAxis != null) {
555 rangeAxis.setPlot(this);
556 rangeAxis.addChangeListener(this);
557 }
558 this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
559
560 configureDomainAxes();
561 configureRangeAxes();
562
563 this.domainGridlinesVisible = true;
564 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
565 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
566
567 this.domainZeroBaselineVisible = false;
568 this.domainZeroBaselinePaint = Color.black;
569 this.domainZeroBaselineStroke = new BasicStroke(0.5f);
570
571 this.rangeGridlinesVisible = true;
572 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
573 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
574
575 this.rangeZeroBaselineVisible = false;
576 this.rangeZeroBaselinePaint = Color.black;
577 this.rangeZeroBaselineStroke = new BasicStroke(0.5f);
578
579 this.domainCrosshairVisible = false;
580 this.domainCrosshairValue = 0.0;
581 this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
582 this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
583
584 this.rangeCrosshairVisible = false;
585 this.rangeCrosshairValue = 0.0;
586 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
587 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
588
589 this.annotations = new java.util.ArrayList();
590
591 }
592
593 /**
594 * Returns the plot type as a string.
595 *
596 * @return A short string describing the type of plot.
597 */
598 public String getPlotType() {
599 return localizationResources.getString("XY_Plot");
600 }
601
602 /**
603 * Returns the orientation of the plot.
604 *
605 * @return The orientation (never <code>null</code>).
606 *
607 * @see #setOrientation(PlotOrientation)
608 */
609 public PlotOrientation getOrientation() {
610 return this.orientation;
611 }
612
613 /**
614 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
615 * all registered listeners.
616 *
617 * @param orientation the orientation (<code>null</code> not allowed).
618 *
619 * @see #getOrientation()
620 */
621 public void setOrientation(PlotOrientation orientation) {
622 if (orientation == null) {
623 throw new IllegalArgumentException("Null 'orientation' argument.");
624 }
625 if (orientation != this.orientation) {
626 this.orientation = orientation;
627 notifyListeners(new PlotChangeEvent(this));
628 }
629 }
630
631 /**
632 * Returns the axis offset.
633 *
634 * @return The axis offset (never <code>null</code>).
635 *
636 * @see #setAxisOffset(RectangleInsets)
637 */
638 public RectangleInsets getAxisOffset() {
639 return this.axisOffset;
640 }
641
642 /**
643 * Sets the axis offsets (gap between the data area and the axes) and sends
644 * a {@link PlotChangeEvent} to all registered listeners.
645 *
646 * @param offset the offset (<code>null</code> not permitted).
647 *
648 * @see #getAxisOffset()
649 */
650 public void setAxisOffset(RectangleInsets offset) {
651 if (offset == null) {
652 throw new IllegalArgumentException("Null 'offset' argument.");
653 }
654 this.axisOffset = offset;
655 notifyListeners(new PlotChangeEvent(this));
656 }
657
658 /**
659 * Returns the domain axis with index 0. If the domain axis for this plot
660 * is <code>null</code>, then the method will return the parent plot's
661 * domain axis (if there is a parent plot).
662 *
663 * @return The domain axis (possibly <code>null</code>).
664 *
665 * @see #getDomainAxis(int)
666 * @see #setDomainAxis(ValueAxis)
667 */
668 public ValueAxis getDomainAxis() {
669 return getDomainAxis(0);
670 }
671
672 /**
673 * Returns the domain axis with the specified index, or <code>null</code>.
674 *
675 * @param index the axis index.
676 *
677 * @return The axis (<code>null</code> possible).
678 *
679 * @see #setDomainAxis(int, ValueAxis)
680 */
681 public ValueAxis getDomainAxis(int index) {
682 ValueAxis result = null;
683 if (index < this.domainAxes.size()) {
684 result = (ValueAxis) this.domainAxes.get(index);
685 }
686 if (result == null) {
687 Plot parent = getParent();
688 if (parent instanceof XYPlot) {
689 XYPlot xy = (XYPlot) parent;
690 result = xy.getDomainAxis(index);
691 }
692 }
693 return result;
694 }
695
696 /**
697 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
698 * to all registered listeners.
699 *
700 * @param axis the new axis (<code>null</code> permitted).
701 *
702 * @see #getDomainAxis()
703 * @see #setDomainAxis(int, ValueAxis)
704 */
705 public void setDomainAxis(ValueAxis axis) {
706 setDomainAxis(0, axis);
707 }
708
709 /**
710 * Sets a domain axis and sends a {@link PlotChangeEvent} to all
711 * registered listeners.
712 *
713 * @param index the axis index.
714 * @param axis the axis (<code>null</code> permitted).
715 *
716 * @see #getDomainAxis(int)
717 * @see #setRangeAxis(int, ValueAxis)
718 */
719 public void setDomainAxis(int index, ValueAxis axis) {
720 setDomainAxis(index, axis, true);
721 }
722
723 /**
724 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
725 * all registered listeners.
726 *
727 * @param index the axis index.
728 * @param axis the axis.
729 * @param notify notify listeners?
730 *
731 * @see #getDomainAxis(int)
732 */
733 public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
734 ValueAxis existing = getDomainAxis(index);
735 if (existing != null) {
736 existing.removeChangeListener(this);
737 }
738 if (axis != null) {
739 axis.setPlot(this);
740 }
741 this.domainAxes.set(index, axis);
742 if (axis != null) {
743 axis.configure();
744 axis.addChangeListener(this);
745 }
746 if (notify) {
747 notifyListeners(new PlotChangeEvent(this));
748 }
749 }
750
751 /**
752 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
753 * to all registered listeners.
754 *
755 * @param axes the axes (<code>null</code> not permitted).
756 *
757 * @see #setRangeAxes(ValueAxis[])
758 */
759 public void setDomainAxes(ValueAxis[] axes) {
760 for (int i = 0; i < axes.length; i++) {
761 setDomainAxis(i, axes[i], false);
762 }
763 notifyListeners(new PlotChangeEvent(this));
764 }
765
766 /**
767 * Returns the location of the primary domain axis.
768 *
769 * @return The location (never <code>null</code>).
770 *
771 * @see #setDomainAxisLocation(AxisLocation)
772 */
773 public AxisLocation getDomainAxisLocation() {
774 return (AxisLocation) this.domainAxisLocations.get(0);
775 }
776
777 /**
778 * Sets the location of the primary domain axis and sends a
779 * {@link PlotChangeEvent} to all registered listeners.
780 *
781 * @param location the location (<code>null</code> not permitted).
782 *
783 * @see #getDomainAxisLocation()
784 */
785 public void setDomainAxisLocation(AxisLocation location) {
786 // delegate...
787 setDomainAxisLocation(0, location, true);
788 }
789
790 /**
791 * Sets the location of the domain axis and, if requested, sends a
792 * {@link PlotChangeEvent} to all registered listeners.
793 *
794 * @param location the location (<code>null</code> not permitted).
795 * @param notify notify listeners?
796 *
797 * @see #getDomainAxisLocation()
798 */
799 public void setDomainAxisLocation(AxisLocation location, boolean notify) {
800 // delegate...
801 setDomainAxisLocation(0, location, notify);
802 }
803
804 /**
805 * Returns the edge for the primary domain axis (taking into account the
806 * plot's orientation).
807 *
808 * @return The edge.
809 *
810 * @see #getDomainAxisLocation()
811 * @see #getOrientation()
812 */
813 public RectangleEdge getDomainAxisEdge() {
814 return Plot.resolveDomainAxisLocation(getDomainAxisLocation(),
815 this.orientation);
816 }
817
818 /**
819 * Returns the number of domain axes.
820 *
821 * @return The axis count.
822 *
823 * @see #getRangeAxisCount()
824 */
825 public int getDomainAxisCount() {
826 return this.domainAxes.size();
827 }
828
829 /**
830 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
831 * to all registered listeners.
832 *
833 * @see #clearRangeAxes()
834 */
835 public void clearDomainAxes() {
836 for (int i = 0; i < this.domainAxes.size(); i++) {
837 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
838 if (axis != null) {
839 axis.removeChangeListener(this);
840 }
841 }
842 this.domainAxes.clear();
843 notifyListeners(new PlotChangeEvent(this));
844 }
845
846 /**
847 * Configures the domain axes.
848 */
849 public void configureDomainAxes() {
850 for (int i = 0; i < this.domainAxes.size(); i++) {
851 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
852 if (axis != null) {
853 axis.configure();
854 }
855 }
856 }
857
858 /**
859 * Returns the location for a domain axis. If this hasn't been set
860 * explicitly, the method returns the location that is opposite to the
861 * primary domain axis location.
862 *
863 * @param index the axis index.
864 *
865 * @return The location (never <code>null</code>).
866 *
867 * @see #setDomainAxisLocation(int, AxisLocation)
868 */
869 public AxisLocation getDomainAxisLocation(int index) {
870 AxisLocation result = null;
871 if (index < this.domainAxisLocations.size()) {
872 result = (AxisLocation) this.domainAxisLocations.get(index);
873 }
874 if (result == null) {
875 result = AxisLocation.getOpposite(getDomainAxisLocation());
876 }
877 return result;
878 }
879
880 /**
881 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
882 * to all registered listeners.
883 *
884 * @param index the axis index.
885 * @param location the location (<code>null</code> not permitted for index
886 * 0).
887 *
888 * @see #getDomainAxisLocation(int)
889 */
890 public void setDomainAxisLocation(int index, AxisLocation location) {
891 // delegate...
892 setDomainAxisLocation(index, location, true);
893 }
894
895 /**
896 * Sets the axis location for a domain axis and, if requested, sends a
897 * {@link PlotChangeEvent} to all registered listeners.
898 *
899 * @param index the axis index.
900 * @param location the location (<code>null</code> not permitted for
901 * index 0).
902 * @param notify notify listeners?
903 *
904 * @since 1.0.5
905 *
906 * @see #getDomainAxisLocation(int)
907 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
908 */
909 public void setDomainAxisLocation(int index, AxisLocation location,
910 boolean notify) {
911
912 if (index == 0 && location == null) {
913 throw new IllegalArgumentException(
914 "Null 'location' for index 0 not permitted.");
915 }
916 this.domainAxisLocations.set(index, location);
917 if (notify) {
918 notifyListeners(new PlotChangeEvent(this));
919 }
920 }
921
922 /**
923 * Returns the edge for a domain axis.
924 *
925 * @param index the axis index.
926 *
927 * @return The edge.
928 *
929 * @see #getRangeAxisEdge(int)
930 */
931 public RectangleEdge getDomainAxisEdge(int index) {
932 AxisLocation location = getDomainAxisLocation(index);
933 RectangleEdge result = Plot.resolveDomainAxisLocation(location,
934 this.orientation);
935 if (result == null) {
936 result = RectangleEdge.opposite(getDomainAxisEdge());
937 }
938 return result;
939 }
940
941 /**
942 * Returns the range axis for the plot. If the range axis for this plot is
943 * <code>null</code>, then the method will return the parent plot's range
944 * axis (if there is a parent plot).
945 *
946 * @return The range axis.
947 *
948 * @see #getRangeAxis(int)
949 * @see #setRangeAxis(ValueAxis)
950 */
951 public ValueAxis getRangeAxis() {
952 return getRangeAxis(0);
953 }
954
955 /**
956 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
957 * all registered listeners.
958 *
959 * @param axis the axis (<code>null</code> permitted).
960 *
961 * @see #getRangeAxis()
962 * @see #setRangeAxis(int, ValueAxis)
963 */
964 public void setRangeAxis(ValueAxis axis) {
965
966 if (axis != null) {
967 axis.setPlot(this);
968 }
969
970 // plot is likely registered as a listener with the existing axis...
971 ValueAxis existing = getRangeAxis();
972 if (existing != null) {
973 existing.removeChangeListener(this);
974 }
975
976 this.rangeAxes.set(0, axis);
977 if (axis != null) {
978 axis.configure();
979 axis.addChangeListener(this);
980 }
981 notifyListeners(new PlotChangeEvent(this));
982
983 }
984
985 /**
986 * Returns the location of the primary range axis.
987 *
988 * @return The location (never <code>null</code>).
989 *
990 * @see #setRangeAxisLocation(AxisLocation)
991 */
992 public AxisLocation getRangeAxisLocation() {
993 return (AxisLocation) this.rangeAxisLocations.get(0);
994 }
995
996 /**
997 * Sets the location of the primary range axis and sends a
998 * {@link PlotChangeEvent} to all registered listeners.
999 *
1000 * @param location the location (<code>null</code> not permitted).
1001 *
1002 * @see #getRangeAxisLocation()
1003 */
1004 public void setRangeAxisLocation(AxisLocation location) {
1005 // delegate...
1006 setRangeAxisLocation(0, location, true);
1007 }
1008
1009 /**
1010 * Sets the location of the primary range axis and, if requested, sends a
1011 * {@link PlotChangeEvent} to all registered listeners.
1012 *
1013 * @param location the location (<code>null</code> not permitted).
1014 * @param notify notify listeners?
1015 *
1016 * @see #getRangeAxisLocation()
1017 */
1018 public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1019 // delegate...
1020 setRangeAxisLocation(0, location, notify);
1021 }
1022
1023 /**
1024 * Returns the edge for the primary range axis.
1025 *
1026 * @return The range axis edge.
1027 *
1028 * @see #getRangeAxisLocation()
1029 * @see #getOrientation()
1030 */
1031 public RectangleEdge getRangeAxisEdge() {
1032 return Plot.resolveRangeAxisLocation(getRangeAxisLocation(),
1033 this.orientation);
1034 }
1035
1036 /**
1037 * Returns a range axis.
1038 *
1039 * @param index the axis index.
1040 *
1041 * @return The axis (<code>null</code> possible).
1042 *
1043 * @see #setRangeAxis(int, ValueAxis)
1044 */
1045 public ValueAxis getRangeAxis(int index) {
1046 ValueAxis result = null;
1047 if (index < this.rangeAxes.size()) {
1048 result = (ValueAxis) this.rangeAxes.get(index);
1049 }
1050 if (result == null) {
1051 Plot parent = getParent();
1052 if (parent instanceof XYPlot) {
1053 XYPlot xy = (XYPlot) parent;
1054 result = xy.getRangeAxis(index);
1055 }
1056 }
1057 return result;
1058 }
1059
1060 /**
1061 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1062 * listeners.
1063 *
1064 * @param index the axis index.
1065 * @param axis the axis (<code>null</code> permitted).
1066 *
1067 * @see #getRangeAxis(int)
1068 */
1069 public void setRangeAxis(int index, ValueAxis axis) {
1070 setRangeAxis(index, axis, true);
1071 }
1072
1073 /**
1074 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
1075 * all registered listeners.
1076 *
1077 * @param index the axis index.
1078 * @param axis the axis (<code>null</code> permitted).
1079 * @param notify notify listeners?
1080 *
1081 * @see #getRangeAxis(int)
1082 */
1083 public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1084 ValueAxis existing = getRangeAxis(index);
1085 if (existing != null) {
1086 existing.removeChangeListener(this);
1087 }
1088 if (axis != null) {
1089 axis.setPlot(this);
1090 }
1091 this.rangeAxes.set(index, axis);
1092 if (axis != null) {
1093 axis.configure();
1094 axis.addChangeListener(this);
1095 }
1096 if (notify) {
1097 notifyListeners(new PlotChangeEvent(this));
1098 }
1099 }
1100
1101 /**
1102 * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1103 * to all registered listeners.
1104 *
1105 * @param axes the axes (<code>null</code> not permitted).
1106 *
1107 * @see #setDomainAxes(ValueAxis[])
1108 */
1109 public void setRangeAxes(ValueAxis[] axes) {
1110 for (int i = 0; i < axes.length; i++) {
1111 setRangeAxis(i, axes[i], false);
1112 }
1113 notifyListeners(new PlotChangeEvent(this));
1114 }
1115
1116 /**
1117 * Returns the number of range axes.
1118 *
1119 * @return The axis count.
1120 *
1121 * @see #getDomainAxisCount()
1122 */
1123 public int getRangeAxisCount() {
1124 return this.rangeAxes.size();
1125 }
1126
1127 /**
1128 * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1129 * to all registered listeners.
1130 *
1131 * @see #clearDomainAxes()
1132 */
1133 public void clearRangeAxes() {
1134 for (int i = 0; i < this.rangeAxes.size(); i++) {
1135 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1136 if (axis != null) {
1137 axis.removeChangeListener(this);
1138 }
1139 }
1140 this.rangeAxes.clear();
1141 notifyListeners(new PlotChangeEvent(this));
1142 }
1143
1144 /**
1145 * Configures the range axes.
1146 *
1147 * @see #configureDomainAxes()
1148 */
1149 public void configureRangeAxes() {
1150 for (int i = 0; i < this.rangeAxes.size(); i++) {
1151 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1152 if (axis != null) {
1153 axis.configure();
1154 }
1155 }
1156 }
1157
1158 /**
1159 * Returns the location for a range axis. If this hasn't been set
1160 * explicitly, the method returns the location that is opposite to the
1161 * primary range axis location.
1162 *
1163 * @param index the axis index.
1164 *
1165 * @return The location (never <code>null</code>).
1166 *
1167 * @see #setRangeAxisLocation(int, AxisLocation)
1168 */
1169 public AxisLocation getRangeAxisLocation(int index) {
1170 AxisLocation result = null;
1171 if (index < this.rangeAxisLocations.size()) {
1172 result = (AxisLocation) this.rangeAxisLocations.get(index);
1173 }
1174 if (result == null) {
1175 result = AxisLocation.getOpposite(getRangeAxisLocation());
1176 }
1177 return result;
1178 }
1179
1180 /**
1181 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1182 * to all registered listeners.
1183 *
1184 * @param index the axis index.
1185 * @param location the location (<code>null</code> permitted).
1186 *
1187 * @see #getRangeAxisLocation(int)
1188 */
1189 public void setRangeAxisLocation(int index, AxisLocation location) {
1190 // delegate...
1191 setRangeAxisLocation(index, location, true);
1192 }
1193
1194 /**
1195 * Sets the axis location for a domain axis and, if requested, sends a
1196 * {@link PlotChangeEvent} to all registered listeners.
1197 *
1198 * @param index the axis index.
1199 * @param location the location (<code>null</code> not permitted for
1200 * index 0).
1201 * @param notify notify listeners?
1202 *
1203 * @since 1.0.5
1204 *
1205 * @see #getRangeAxisLocation(int)
1206 * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1207 */
1208 public void setRangeAxisLocation(int index, AxisLocation location,
1209 boolean notify) {
1210
1211 if (index == 0 && location == null) {
1212 throw new IllegalArgumentException(
1213 "Null 'location' for index 0 not permitted.");
1214 }
1215 this.rangeAxisLocations.set(index, location);
1216 if (notify) {
1217 notifyListeners(new PlotChangeEvent(this));
1218 }
1219 }
1220
1221 /**
1222 * Returns the edge for a range axis.
1223 *
1224 * @param index the axis index.
1225 *
1226 * @return The edge.
1227 *
1228 * @see #getRangeAxisLocation(int)
1229 * @see #getOrientation()
1230 */
1231 public RectangleEdge getRangeAxisEdge(int index) {
1232 AxisLocation location = getRangeAxisLocation(index);
1233 RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1234 this.orientation);
1235 if (result == null) {
1236 result = RectangleEdge.opposite(getRangeAxisEdge());
1237 }
1238 return result;
1239 }
1240
1241 /**
1242 * Returns the primary dataset for the plot.
1243 *
1244 * @return The primary dataset (possibly <code>null</code>).
1245 *
1246 * @see #getDataset(int)
1247 * @see #setDataset(XYDataset)
1248 */
1249 public XYDataset getDataset() {
1250 return getDataset(0);
1251 }
1252
1253 /**
1254 * Returns a dataset.
1255 *
1256 * @param index the dataset index.
1257 *
1258 * @return The dataset (possibly <code>null</code>).
1259 *
1260 * @see #setDataset(int, XYDataset)
1261 */
1262 public XYDataset getDataset(int index) {
1263 XYDataset result = null;
1264 if (this.datasets.size() > index) {
1265 result = (XYDataset) this.datasets.get(index);
1266 }
1267 return result;
1268 }
1269
1270 /**
1271 * Sets the primary dataset for the plot, replacing the existing dataset if
1272 * there is one.
1273 *
1274 * @param dataset the dataset (<code>null</code> permitted).
1275 *
1276 * @see #getDataset()
1277 * @see #setDataset(int, XYDataset)
1278 */
1279 public void setDataset(XYDataset dataset) {
1280 setDataset(0, dataset);
1281 }
1282
1283 /**
1284 * Sets a dataset for the plot.
1285 *
1286 * @param index the dataset index.
1287 * @param dataset the dataset (<code>null</code> permitted).
1288 *
1289 * @see #getDataset(int)
1290 */
1291 public void setDataset(int index, XYDataset dataset) {
1292 XYDataset existing = getDataset(index);
1293 if (existing != null) {
1294 existing.removeChangeListener(this);
1295 }
1296 this.datasets.set(index, dataset);
1297 if (dataset != null) {
1298 dataset.addChangeListener(this);
1299 }
1300
1301 // send a dataset change event to self...
1302 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1303 datasetChanged(event);
1304 }
1305
1306 /**
1307 * Returns the number of datasets.
1308 *
1309 * @return The number of datasets.
1310 */
1311 public int getDatasetCount() {
1312 return this.datasets.size();
1313 }
1314
1315 /**
1316 * Returns the index of the specified dataset, or <code>-1</code> if the
1317 * dataset does not belong to the plot.
1318 *
1319 * @param dataset the dataset (<code>null</code> not permitted).
1320 *
1321 * @return The index.
1322 */
1323 public int indexOf(XYDataset dataset) {
1324 int result = -1;
1325 for (int i = 0; i < this.datasets.size(); i++) {
1326 if (dataset == this.datasets.get(i)) {
1327 result = i;
1328 break;
1329 }
1330 }
1331 return result;
1332 }
1333
1334 /**
1335 * Maps a dataset to a particular domain axis. All data will be plotted
1336 * against axis zero by default, no mapping is required for this case.
1337 *
1338 * @param index the dataset index (zero-based).
1339 * @param axisIndex the axis index.
1340 *
1341 * @see #mapDatasetToRangeAxis(int, int)
1342 */
1343 public void mapDatasetToDomainAxis(int index, int axisIndex) {
1344 this.datasetToDomainAxisMap.put(new Integer(index),
1345 new Integer(axisIndex));
1346 // fake a dataset change event to update axes...
1347 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1348 }
1349
1350 /**
1351 * Maps a dataset to a particular range 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 #mapDatasetToDomainAxis(int, int)
1358 */
1359 public void mapDatasetToRangeAxis(int index, int axisIndex) {
1360 this.datasetToRangeAxisMap.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 * Returns the renderer for the primary dataset.
1368 *
1369 * @return The item renderer (possibly <code>null</code>).
1370 *
1371 * @see #setRenderer(XYItemRenderer)
1372 */
1373 public XYItemRenderer getRenderer() {
1374 return getRenderer(0);
1375 }
1376
1377 /**
1378 * Returns the renderer for a dataset, or <code>null</code>.
1379 *
1380 * @param index the renderer index.
1381 *
1382 * @return The renderer (possibly <code>null</code>).
1383 *
1384 * @see #setRenderer(int, XYItemRenderer)
1385 */
1386 public XYItemRenderer getRenderer(int index) {
1387 XYItemRenderer result = null;
1388 if (this.renderers.size() > index) {
1389 result = (XYItemRenderer) this.renderers.get(index);
1390 }
1391 return result;
1392
1393 }
1394
1395 /**
1396 * Sets the renderer for the primary dataset and sends a
1397 * {@link PlotChangeEvent} to all registered listeners. If the renderer
1398 * is set to <code>null</code>, no data will be displayed.
1399 *
1400 * @param renderer the renderer (<code>null</code> permitted).
1401 *
1402 * @see #getRenderer()
1403 */
1404 public void setRenderer(XYItemRenderer renderer) {
1405 setRenderer(0, renderer);
1406 }
1407
1408 /**
1409 * Sets a renderer and sends a {@link PlotChangeEvent} to all
1410 * registered listeners.
1411 *
1412 * @param index the index.
1413 * @param renderer the renderer.