KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > XYPlot


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * -----------
27  * XYPlot.java
28  * -----------
29  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
30  *
31  * Original Author: David Gilbert (for Object Refinery Limited);
32  * Contributor(s): Craig MacFarlane;
33  * Mark Watson (www.markwatson.com);
34  * Jonathan Nash;
35  * Gideon Krause;
36  * Klaus Rheinwald;
37  * Xavier Poinsard;
38  * Richard Atkinson;
39  * Arnaud Lelievre;
40  * Nicolas Brodu;
41  * Eduardo Ramalho;
42  *
43  * $Id: XYPlot.java,v 1.44 2005/06/01 11:00:40 mungady Exp $
44  *
45  * Changes (from 21-Jun-2001)
46  * --------------------------
47  * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
48  * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
49  * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
50  * 19-Oct-2001 : Removed the code for drawing the visual representation of each
51  * data point into a separate class StandardXYItemRenderer.
52  * This will make it easier to add variations to the way the
53  * charts are drawn. Based on code contributed by Mark
54  * Watson (DG);
55  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
56  * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
57  * inside JScrollPane (DG);
58  * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
59  * 13-Dec-2001 : Added skeleton code for tooltips. Added new constructor. (DG);
60  * 16-Jan-2002 : Renamed the tooltips class (DG);
61  * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
62  * Crosshairs based on code by Jonathan Nash (DG);
63  * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
64  * Vieujot (DG);
65  * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
66  * special case when chart is null (DG);
67  * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
68  * 28-Mar-2002 : The plot now registers with the renderer as a property change
69  * listener. Also added a new constructor (DG);
70  * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
71  * method. Moved the tooltip generator into the renderer (DG);
72  * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
73  * lines (DG);
74  * 13-May-2002 : Small change to the draw() method so that it works for
75  * OverlaidXYPlot also (DG);
76  * 25-Jun-2002 : Removed redundant import (DG);
77  * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
78  * setXYItemRenderer() --> setRenderer() (DG);
79  * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
80  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
81  * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
82  * these were set in the axes) (DG);
83  * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
84  * border bug fix contributed by Gideon Krause (DG);
85  * 22-Jan-2003 : Removed monolithic constructor (DG);
86  * 04-Mar-2003 : Added 'no data' message, see bug report 691634. Added
87  * secondary range markers using code contributed by Klaus
88  * Rheinwald (DG);
89  * 26-Mar-2003 : Implemented Serializable (DG);
90  * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
91  * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
92  * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
93  * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
94  * 15-May-2003 : Added an orientation attribute (DG);
95  * 02-Jun-2003 : Removed range axis compatibility test (DG);
96  * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
97  * Services Ltd) (DG);
98  * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
99  * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
100  * overlaid plots) (DG);
101  * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
102  * renderers (DG);
103  * 27-Jul-2003 : Added support for stacked XY area charts (RA);
104  * 19-Aug-2003 : Implemented Cloneable (DG);
105  * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
106  * change event (797466) (DG)
107  * 08-Sep-2003 : Added internationalization via use of properties
108  * resourceBundle (RFE 690236) (AL);
109  * 08-Sep-2003 : Changed ValueAxis API (DG);
110  * 08-Sep-2003 : Fixes for serialization (NB);
111  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
112  * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
113  * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
114  * getSecondaryRangeAxisCount() methods suggested by Eduardo
115  * Ramalho (RFE 808548) (DG);
116  * 23-Sep-2003 : Split domain and range markers into foreground and
117  * background (DG);
118  * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
119  * methods. Fixed bug (815876) in addSecondaryRangeMarker()
120  * method. Added new addSecondaryDomainMarker methods (see bug
121  * id 815869) (DG);
122  * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
123  * requested by Eduardo Ramalho (DG);
124  * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
125  * values (DG);
126  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
127  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
128  * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
129  * range type (DG);
130  * 22-Mar-2004 : Fixed cloning bug (DG);
131  * 23-Mar-2004 : Fixed more cloning bugs (DG);
132  * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
133  * stacked, see this post in the forum:
134  * http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
135  * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
136  * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
137  * plot (DG);
138  * 27-Apr-2004 : Removed major distinction between primary and secondary
139  * datasets, renderers and axes (DG);
140  * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
141  * renderer interface (DG);
142  * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
143  * 19-May-2004 : Added indexOf() method (DG);
144  * 03-Jun-2004 : Fixed zooming bug (DG);
145  * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
146  * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
147  * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
148  * the x-value range (now matches behaviour for y-values). Added
149  * getDomainAxisIndex() method (DG);
150  * 12-Nov-2004 : Implemented new Zoomable interface (DG);
151  * 25-Nov-2004 : Small update to clone() implementation (DG);
152  * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
153  * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
154  * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
155  * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
156  * 26-Apr-2005 : Removed LOGGER (DG);
157  * 04-May-2005 : Fixed serialization of domain and range markers (DG);
158  * 05-May-2005 : Removed unused draw() method (DG);
159  * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
160  * RFE 1183100 (DG);
161  * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
162  * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
163  * 01-Jun-2005 : Added clearDomainMarkers(int) method to match
164  * clearRangeMarkers(int) (DG);
165  *
166  */

167
168 package org.jfree.chart.plot;
169
170 import java.awt.AlphaComposite JavaDoc;
171 import java.awt.BasicStroke JavaDoc;
172 import java.awt.Color JavaDoc;
173 import java.awt.Composite JavaDoc;
174 import java.awt.Graphics2D JavaDoc;
175 import java.awt.Paint JavaDoc;
176 import java.awt.Shape JavaDoc;
177 import java.awt.Stroke JavaDoc;
178 import java.awt.geom.Line2D JavaDoc;
179 import java.awt.geom.Point2D JavaDoc;
180 import java.awt.geom.Rectangle2D JavaDoc;
181 import java.io.IOException JavaDoc;
182 import java.io.ObjectInputStream JavaDoc;
183 import java.io.ObjectOutputStream JavaDoc;
184 import java.io.Serializable JavaDoc;
185 import java.util.ArrayList JavaDoc;
186 import java.util.Collection JavaDoc;
187 import java.util.Collections JavaDoc;
188 import java.util.HashMap JavaDoc;
189 import java.util.Iterator JavaDoc;
190 import java.util.List JavaDoc;
191 import java.util.Map JavaDoc;
192 import java.util.ResourceBundle JavaDoc;
193 import java.util.TreeMap JavaDoc;
194
195 import org.jfree.chart.LegendItem;
196 import org.jfree.chart.LegendItemCollection;
197 import org.jfree.chart.annotations.XYAnnotation;
198 import org.jfree.chart.axis.Axis;
199 import org.jfree.chart.axis.AxisCollection;
200 import org.jfree.chart.axis.AxisLocation;
201 import org.jfree.chart.axis.AxisSpace;
202 import org.jfree.chart.axis.AxisState;
203 import org.jfree.chart.axis.ValueAxis;
204 import org.jfree.chart.axis.ValueTick;
205 import org.jfree.chart.event.ChartChangeEventType;
206 import org.jfree.chart.event.PlotChangeEvent;
207 import org.jfree.chart.event.RendererChangeEvent;
208 import org.jfree.chart.event.RendererChangeListener;
209 import org.jfree.chart.renderer.xy.XYItemRenderer;
210 import org.jfree.chart.renderer.xy.XYItemRendererState;
211 import org.jfree.data.Range;
212 import org.jfree.data.general.Dataset;
213 import org.jfree.data.general.DatasetChangeEvent;
214 import org.jfree.data.general.DatasetUtilities;
215 import org.jfree.data.xy.XYDataset;
216 import org.jfree.io.SerialUtilities;
217 import org.jfree.ui.Layer;
218 import org.jfree.ui.RectangleEdge;
219 import org.jfree.ui.RectangleInsets;
220 import org.jfree.util.ObjectList;
221 import org.jfree.util.ObjectUtilities;
222 import org.jfree.util.PublicCloneable;
223
224 /**
225  * A general class for plotting data in the form of (x, y) pairs. This plot can
226  * use data from any class that implements the {@link XYDataset} interface.
227  * <P>
228  * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
229  * on the plot. By using different renderers, various chart types can be
230  * produced.
231  * <p>
232  * The {@link org.jfree.chart.ChartFactory} class contains static methods for
233  * creating pre-configured charts.
234  */

235 public class XYPlot extends Plot implements ValueAxisPlot,
236                                             Zoomable,
237                                             RendererChangeListener,
238                                             Cloneable JavaDoc, PublicCloneable,
239                                             Serializable JavaDoc {
240
241     /** For serialization. */
242     private static final long serialVersionUID = 7044148245716569264L;
243     
244     /** The default grid line stroke. */
245     public static final Stroke JavaDoc DEFAULT_GRIDLINE_STROKE = new BasicStroke JavaDoc(
246         0.5f,
247         BasicStroke.CAP_BUTT,
248         BasicStroke.JOIN_BEVEL,
249         0.0f,
250         new float[] {2.0f, 2.0f},
251         0.0f
252     );
253
254     /** The default grid line paint. */
255     public static final Paint JavaDoc DEFAULT_GRIDLINE_PAINT = Color.lightGray;
256
257     /** The default crosshair visibility. */
258     public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
259
260     /** The default crosshair stroke. */
261     public static final Stroke JavaDoc DEFAULT_CROSSHAIR_STROKE
262         = DEFAULT_GRIDLINE_STROKE;
263
264     /** The default crosshair paint. */
265     public static final Paint JavaDoc DEFAULT_CROSSHAIR_PAINT = Color.blue;
266
267     /** The resourceBundle for the localization. */
268     protected static ResourceBundle JavaDoc localizationResources
269         = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
270
271     /** The plot orientation. */
272     private PlotOrientation orientation;
273
274     /** The offset between the data area and the axes. */
275     private RectangleInsets axisOffset;
276
277     /** The domain axis / axes (used for the x-values). */
278     private ObjectList domainAxes;
279
280     /** The domain axis locations. */
281     private ObjectList domainAxisLocations;
282
283     /** The range axis (used for the y-values). */
284     private ObjectList rangeAxes;
285
286     /** The range axis location. */
287     private ObjectList rangeAxisLocations;
288
289     /** Storage for the datasets. */
290     private ObjectList datasets;
291
292     /** Storage for the renderers. */
293     private ObjectList renderers;
294
295     /**
296      * Storage for keys that map datasets/renderers to domain axes. If the
297      * map contains no entry for a dataset, it is assumed to map to the
298      * primary domain axis (index = 0).
299      */

300     private Map JavaDoc datasetToDomainAxisMap;
301
302     /**
303      * Storage for keys that map datasets/renderers to range axes. If the
304      * map contains no entry for a dataset, it is assumed to map to the
305      * primary domain axis (index = 0).
306      */

307     private Map JavaDoc datasetToRangeAxisMap;
308
309     /** The origin point for the quadrants (if drawn). */
310     private transient Point2D JavaDoc quadrantOrigin = new Point2D.Double JavaDoc(0.0, 0.0);
311
312     /** The paint used for each quadrant. */
313     private transient Paint JavaDoc[] quadrantPaint
314         = new Paint JavaDoc[] {null, null, null, null};
315
316     /** A flag that controls whether the domain grid-lines are visible. */
317     private boolean domainGridlinesVisible;
318
319     /** The stroke used to draw the domain grid-lines. */
320     private transient Stroke JavaDoc domainGridlineStroke;
321
322     /** The paint used to draw the domain grid-lines. */
323     private transient Paint JavaDoc domainGridlinePaint;
324
325     /** A flag that controls whether the range grid-lines are visible. */
326     private boolean rangeGridlinesVisible;
327
328     /** The stroke used to draw the range grid-lines. */
329     private transient Stroke JavaDoc rangeGridlineStroke;
330
331     /** The paint used to draw the range grid-lines. */
332     private transient Paint JavaDoc rangeGridlinePaint;
333
334     /**
335      * A flag that controls whether or not the zero baseline against the range
336      * axis is visible.
337      */

338     private boolean rangeZeroBaselineVisible;
339
340     /** The stroke used for the zero baseline against the range axis. */
341     private transient Stroke JavaDoc rangeZeroBaselineStroke;
342
343     /** The paint used for the zero baseline against the range axis. */
344     private transient Paint JavaDoc rangeZeroBaselinePaint;
345
346     /** A flag that controls whether or not a domain crosshair is drawn..*/
347     private boolean domainCrosshairVisible;
348
349     /** The domain crosshair value. */
350     private double domainCrosshairValue;
351
352     /** The pen/brush used to draw the crosshair (if any). */
353     private transient Stroke JavaDoc domainCrosshairStroke;
354
355     /** The color used to draw the crosshair (if any). */
356     private transient Paint JavaDoc domainCrosshairPaint;
357
358     /**
359      * A flag that controls whether or not the crosshair locks onto actual
360      * data points.
361      */

362     private boolean domainCrosshairLockedOnData = true;
363
364     /** A flag that controls whether or not a range crosshair is drawn..*/
365     private boolean rangeCrosshairVisible;
366
367     /** The range crosshair value. */
368     private double rangeCrosshairValue;
369
370     /** The pen/brush used to draw the crosshair (if any). */
371     private transient Stroke JavaDoc rangeCrosshairStroke;
372
373     /** The color used to draw the crosshair (if any). */
374     private transient Paint JavaDoc rangeCrosshairPaint;
375
376     /**
377      * A flag that controls whether or not the crosshair locks onto actual
378      * data points.
379      */

380     private boolean rangeCrosshairLockedOnData = true;
381
382     /** A map of lists of foreground markers (optional) for the domain axes. */
383     private Map JavaDoc foregroundDomainMarkers;
384
385     /** A map of lists of background markers (optional) for the domain axes. */
386     private Map JavaDoc backgroundDomainMarkers;
387
388     /** A map of lists of foreground markers (optional) for the range axes. */
389     private Map JavaDoc foregroundRangeMarkers;
390
391     /** A map of lists of background markers (optional) for the range axes. */
392     private Map JavaDoc backgroundRangeMarkers;
393
394     /**
395      * A (possibly empty) list of annotations for the plot. The list should
396      * be initialised in the constructor and never allowed to be
397      * <code>null</code>.
398      */

399     private List JavaDoc annotations;
400
401     /** The paint used for the domain tick bands (if any). */
402     private transient Paint JavaDoc domainTickBandPaint;
403
404     /** The paint used for the range tick bands (if any). */
405     private transient Paint JavaDoc rangeTickBandPaint;
406
407     /** The fixed domain axis space. */
408     private AxisSpace fixedDomainAxisSpace;
409
410     /** The fixed range axis space. */
411     private AxisSpace fixedRangeAxisSpace;
412
413     /**
414      * The order of the dataset rendering (REVERSE draws the primary dataset
415      * last so that it appears to be on top).
416      */

417     private DatasetRenderingOrder datasetRenderingOrder
418         = DatasetRenderingOrder.REVERSE;
419
420     /**
421      * The order of the series rendering (REVERSE draws the primary series
422      * last so that it appears to be on top).
423      */

424     private SeriesRenderingOrder seriesRenderingOrder
425         = SeriesRenderingOrder.REVERSE;
426
427     /**
428      * The weight for this plot (only relevant if this is a subplot in a
429      * combined plot).
430      */

431     private int weight;
432
433     /**
434      * An optional collection of legend items that can be returned by the
435      * getLegendItems() method.
436      */

437     private LegendItemCollection fixedLegendItems;
438
439     /**
440      * Default constructor.
441      */

442     public XYPlot() {
443         this(null, null, null, null);
444     }
445
446     /**
447      * Creates a new plot.
448      *
449      * @param dataset the dataset (<code>null</code> permitted).
450      * @param domainAxis the domain axis (<code>null</code> permitted).
451      * @param rangeAxis the range axis (<code>null</code> permitted).
452      * @param renderer the renderer (<code>null</code> permitted).
453      */

454     public XYPlot(XYDataset dataset,
455                   ValueAxis domainAxis,
456                   ValueAxis rangeAxis,
457                   XYItemRenderer renderer) {
458
459         super();
460
461         this.orientation = PlotOrientation.VERTICAL;
462         this.weight = 1; // only relevant when this is a subplot
463
this.axisOffset = RectangleInsets.ZERO_INSETS;
464
465         // allocate storage for datasets, axes and renderers (all optional)
466
this.domainAxes = new ObjectList();
467         this.domainAxisLocations = new ObjectList();
468         this.foregroundDomainMarkers = new HashMap JavaDoc();
469         this.backgroundDomainMarkers = new HashMap JavaDoc();
470
471         this.rangeAxes = new ObjectList();
472         this.rangeAxisLocations = new ObjectList();
473         this.foregroundRangeMarkers = new HashMap JavaDoc();
474         this.backgroundRangeMarkers = new HashMap JavaDoc();
475
476         this.datasets = new ObjectList();
477         this.renderers = new ObjectList();
478
479         this.datasetToDomainAxisMap = new TreeMap JavaDoc();
480         this.datasetToRangeAxisMap = new TreeMap JavaDoc();
481
482         this.datasets.set(0, dataset);
483         if (dataset != null) {
484             dataset.addChangeListener(this);
485         }
486
487         this.renderers.set(0, renderer);
488         if (renderer != null) {
489             renderer.setPlot(this);
490             renderer.addChangeListener(this);
491         }
492
493         this.domainAxes.set(0, domainAxis);
494         this.mapDatasetToDomainAxis(0, 0);
495         if (domainAxis != null) {
496             domainAxis.setPlot(this);
497             domainAxis.addChangeListener(this);
498         }
499         this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
500
501         this.rangeAxes.set(0, rangeAxis);
502         this.mapDatasetToRangeAxis(0, 0);
503         if (rangeAxis != null) {
504             rangeAxis.setPlot(this);
505             rangeAxis.addChangeListener(this);
506         }
507         this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
508
509         configureDomainAxes();
510         configureRangeAxes();
511
512         this.domainGridlinesVisible = true;
513         this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
514         this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
515
516         this.rangeGridlinesVisible = true;
517         this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
518         this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
519
520         this.rangeZeroBaselineVisible = false;
521         this.rangeZeroBaselinePaint = Color.black;
522         this.rangeZeroBaselineStroke = new BasicStroke JavaDoc(0.5f);
523
524         this.domainCrosshairVisible = false;
525         this.domainCrosshairValue = 0.0;
526         this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
527         this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
528
529         this.rangeCrosshairVisible = false;
530         this.rangeCrosshairValue = 0.0;
531         this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
532         this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
533
534         this.annotations = new java.util.ArrayList JavaDoc();
535
536     }
537
538     /**
539      * Returns the plot type as a string.
540      *
541      * @return A short string describing the type of plot.
542      */

543     public String JavaDoc getPlotType() {
544         return localizationResources.getString("XY_Plot");
545     }
546
547     /**
548      * Returns the orientation of the plot.
549      *
550      * @return The orientation of the plot.
551      */

552     public PlotOrientation getOrientation() {
553         return this.orientation;
554     }
555
556     /**
557      * Sets the orientation for the plot.
558      *
559      * @param orientation the orientation (<code>null</code> not allowed).
560      */

561     public void setOrientation(PlotOrientation orientation) {
562         if (orientation == null) {
563             throw new IllegalArgumentException JavaDoc("Null 'orientation' argument.");
564         }
565         if (orientation != this.orientation) {
566             this.orientation = orientation;
567             notifyListeners(new PlotChangeEvent(this));
568         }
569     }
570
571     /**
572      * Returns the axis offset.
573      *
574      * @return The axis offset (never <code>null</code>).
575      */

576     public RectangleInsets getAxisOffset() {
577         return this.axisOffset;
578     }
579
580     /**
581      * Sets the axis offsets (gap between the data area and the axes).
582      *
583      * @param offset the offset (<code>null</code> not permitted).
584      */

585     public void setAxisOffset(RectangleInsets offset) {
586         if (offset == null) {
587             throw new IllegalArgumentException JavaDoc("Null 'offset' argument.");
588         }
589         this.axisOffset = offset;
590         notifyListeners(new PlotChangeEvent(this));
591     }
592
593     /**
594      * Returns the domain axis for the plot. If the domain axis for this plot
595      * is null, then the method will return the parent plot's domain axis (if
596      * there is a parent plot).
597      *
598      * @return The domain axis.
599      */

600     public ValueAxis getDomainAxis() {
601         return getDomainAxis(0);
602     }
603
604     /**
605      * Returns a domain axis.
606      *
607      * @param index the axis index.
608      *
609      * @return The axis (<code>null</code> possible).
610      */

611     public ValueAxis getDomainAxis(int index) {
612         ValueAxis result = null;
613         if (index < this.domainAxes.size()) {
614             result = (ValueAxis) this.domainAxes.get(index);
615         }
616         if (result == null) {
617             Plot parent = getParent();
618             if (parent instanceof XYPlot) {
619                 XYPlot xy = (XYPlot) parent;
620                 result = xy.getDomainAxis(index);
621             }
622         }
623         return result;
624     }
625
626     /**
627      * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
628      * to all registered listeners.
629      *
630      * @param axis the new axis (<code>null</code> permitted).
631      */

632     public void setDomainAxis(ValueAxis axis) {
633         setDomainAxis(0, axis);
634     }
635
636     /**
637      * Sets a domain axis and sends a {@link PlotChangeEvent} to all
638      * registered listeners.
639      *
640      * @param index the axis index.
641      * @param axis the axis.
642      */

643     public void setDomainAxis(int index, ValueAxis axis) {
644         setDomainAxis(index, axis, true);
645     }
646     
647     /**
648      * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
649      * all registered listeners.
650      *
651      * @param index the axis index.
652      * @param axis the axis.
653      * @param notify notify listeners?
654      */

655     public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
656         ValueAxis existing = getDomainAxis(index);
657         if (existing != null) {
658             existing.removeChangeListener(this);
659         }
660         if (axis != null) {
661             axis.setPlot(this);
662         }
663         this.domainAxes.set(index, axis);
664         if (axis != null) {
665             axis.configure();
666             axis.addChangeListener(this);
667         }
668         if (notify) {
669             notifyListeners(new PlotChangeEvent(this));
670         }
671     }
672
673     /**
674      * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
675      * to all registered listeners.
676      *
677      * @param axes the axes.
678      */

679     public void setDomainAxes(ValueAxis[] axes) {
680         for (int i = 0; i < axes.length; i++) {
681             setDomainAxis(i, axes[i], false);
682         }
683         notifyListeners(new PlotChangeEvent(this));
684     }
685     
686     /**
687      * Returns the location of the primary domain axis.
688      *
689      * @return The location (never <code>null</code>).
690      */

691     public AxisLocation getDomainAxisLocation() {
692         return (AxisLocation) this.domainAxisLocations.get(0);
693     }
694
695     /**
696      * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
697      * to all registered listeners.
698      *
699      * @param location the location (<code>null</code> not permitted).
700      */

701     public void setDomainAxisLocation(AxisLocation location) {
702         // defer argument checking...
703
setDomainAxisLocation(location, true);
704     }
705
706     /**
707      * Sets the location of the domain axis and, if requested, sends a
708      * {@link PlotChangeEvent} to all registered listeners.
709      *
710      * @param location the location (<code>null</code> not permitted).
711      * @param notify notify listeners?
712      */

713     public void setDomainAxisLocation(AxisLocation location, boolean notify) {
714         if (location == null) {
715             throw new IllegalArgumentException JavaDoc("Null 'location' argument.");
716         }
717         this.domainAxisLocations.set(0, location);
718         if (notify) {
719             notifyListeners(new PlotChangeEvent(this));
720         }
721     }
722
723     /**
724      * Returns the edge for the primary domain axis (taking into account the
725      * plot's orientation.
726      *
727      * @return The edge.
728      */

729     public RectangleEdge getDomainAxisEdge() {
730         return Plot.resolveDomainAxisLocation(
731             getDomainAxisLocation(), this.orientation
732         );
733     }
734
735     /**
736      * Returns the number of domain axes.
737      *
738      * @return The axis count.
739      */

740     public int getDomainAxisCount() {
741         return this.domainAxes.size();
742     }
743
744     /**
745      * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
746      * to all registered listeners.
747      */

748     public void clearDomainAxes() {
749         for (int i = 0; i < this.domainAxes.size(); i++) {
750             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
751             if (axis != null) {
752                 axis.removeChangeListener(this);
753             }
754         }
755         this.domainAxes.clear();
756         notifyListeners(new PlotChangeEvent(this));
757     }
758
759     /**
760      * Configures the domain axes.
761      */

762     public void configureDomainAxes() {
763         for (int i = 0; i < this.domainAxes.size(); i++) {
764             ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
765             if (axis != null) {
766                 axis.configure();
767             }
768         }
769     }
770
771     /**
772      * Returns the location for a domain axis. If this hasn't been set
773      * explicitly, the method returns the location that is opposite to the
774      * primary domain axis location.
775      *
776      * @param index the axis index.
777      *
778      * @return The location (never <code>null</code>).
779      */

780     public AxisLocation getDomainAxisLocation(int index) {
781         AxisLocation result = null;
782         if (index < this.domainAxisLocations.size()) {
783             result = (AxisLocation) this.domainAxisLocations.get(index);
784         }
785         if (result == null) {
786             result = AxisLocation.getOpposite(getDomainAxisLocation());
787         }
788         return result;
789     }
790
791     /**
792      * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
793      * to all registered listeners.
794      *
795      * @param index the axis index.
796      * @param location the location (<code>null</code> permitted).
797      */

798     public void setDomainAxisLocation(int index, AxisLocation location) {
799         this.domainAxisLocations.set(index, location);
800         notifyListeners(new PlotChangeEvent(this));
801     }
802
803     /**
804      * Returns the edge for a domain axis.
805      *
806      * @param index the axis index.
807      *
808      * @return The edge.
809      */

810     public RectangleEdge getDomainAxisEdge(int index) {
811         AxisLocation location = getDomainAxisLocation(index);
812         RectangleEdge result = Plot.resolveDomainAxisLocation(
813             location, this.orientation
814         );
815         if (result == null) {
816             result = RectangleEdge.opposite(getDomainAxisEdge());
817         }
818         return result;
819     }
820
821     /**
822      * Returns the range axis for the plot. If the range axis for this plot is
823      * null, then the method will return the parent plot's range axis (if
824      * there is a parent plot).
825      *
826      * @return The range axis.
827      */

828     public ValueAxis getRangeAxis() {
829         return getRangeAxis(0);
830     }
831
832     /**
833      * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
834      * all registered listeners.
835      *
836      * @param axis the axis (<code>null</code> permitted).
837      *
838      */

839     public void setRangeAxis(ValueAxis axis) {
840
841         if (axis != null) {
842             axis.setPlot(this);
843         }
844
845         // plot is likely registered as a listener with the existing axis...
846
ValueAxis existing = getRangeAxis();
847         if (existing != null) {
848             existing.removeChangeListener(this);
849         }
850
851         this.rangeAxes.set(0, axis);
852         if (axis != null) {
853             axis.configure();
854             axis.addChangeListener(this);
855         }
856         notifyListeners(new PlotChangeEvent(this));
857
858     }
859
860     /**
861      * Returns the location of the primary range axis.
862      *
863      * @return The location (never <code>null</code>).
864      */

865     public AxisLocation getRangeAxisLocation() {
866         return (AxisLocation) this.rangeAxisLocations.get(0);
867     }
868
869     /**
870      * Sets the location of the primary range axis and sends a
871      * {@link PlotChangeEvent} to all registered listeners.
872      *
873      * @param location the location (<code>null</code> not permitted).
874      */

875     public void setRangeAxisLocation(AxisLocation location) {
876         // defer argument checking...
877
setRangeAxisLocation(location, true);
878     }
879
880     /**
881      * Sets the location of the primary range axis and, if requested, sends a
882      * {@link PlotChangeEvent} to all registered listeners.
883      *
884      * @param location the location (<code>null</code> not permitted).
885      * @param notify notify listeners?
886      */

887     public void setRangeAxisLocation(AxisLocation location, boolean notify) {
888         if (location == null) {
889             throw new IllegalArgumentException JavaDoc("Null 'location' argument.");
890         }
891         this.rangeAxisLocations.set(0, location);
892         if (notify) {
893             notifyListeners(new PlotChangeEvent(this));
894         }
895
896     }
897
898     /**
899      * Returns the edge for the primary range axis.
900      *
901      * @return The range axis edge.
902      */

903     public RectangleEdge getRangeAxisEdge() {
904         return Plot.resolveRangeAxisLocation(
905             getRangeAxisLocation(), this.orientation
906         );
907     }
908
909     /**
910      * Returns a range axis.
911      *
912      * @param index the axis index.
913      *
914      * @return The axis (<code>null</code> possible).
915      */

916     public ValueAxis getRangeAxis(int index) {
917         ValueAxis result = null;
918         if (index < this.rangeAxes.size()) {
919             result = (ValueAxis) this.rangeAxes.get(index);
920         }
921         if (result == null) {
922             Plot parent = getParent();
923             if (parent instanceof XYPlot) {
924                 XYPlot xy = (XYPlot) parent;
925                 result = xy.getRangeAxis(index);
926             }
927         }
928         return result;
929     }
930
931     /**
932      * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
933      * listeners.
934      *
935      * @param index the axis index.
936      * @param axis the axis (<code>null</code> permitted).
937      */

938     public void setRangeAxis(int index, ValueAxis axis) {
939         setRangeAxis(index, axis, true);
940     }
941     
942     /**
943      * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
944      * all registered listeners.
945      *
946      * @param index the axis index.
947      * @param axis the axis (<code>null</code> permitted).
948      */

949     public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
950         ValueAxis existing = getRangeAxis(index);
951         if (existing != null) {
952             existing.removeChangeListener(this);
953         }
954         if (axis != null) {
955             axis.setPlot(this);
956         }
957         this.rangeAxes.set(index, axis);
958         if (axis != null) {
959             axis.configure();
960             axis.addChangeListener(this);
961         }
962         if (notify) {
963             notifyListeners(new PlotChangeEvent(this));
964         }
965     }
966
967     /**
968      * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
969      * to all registered listeners.
970      *
971      * @param axes the axes.
972      */

973     public void setRangeAxes(ValueAxis[] axes) {
974         for (int i = 0; i < axes.length; i++) {
975             setRangeAxis(i, axes[i], false);
976         }
977         notifyListeners(new PlotChangeEvent(this));
978     }
979     
980     /**
981      * Returns the number of range axes.
982      *
983      * @return The axis count.
984      */

985     public int getRangeAxisCount() {
986         return this.rangeAxes.size();
987     }
988
989     /**
990      * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
991      * to all registered listeners.
992      */

993     public void clearRangeAxes() {
994         for (int i = 0; i < this.rangeAxes.size(); i++) {
995             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
996             if (axis != null) {
997                 axis.removeChangeListener(this);
998             }
999         }
1000        this.rangeAxes.clear();
1001        notifyListeners(new PlotChangeEvent(this));
1002    }
1003
1004    /**
1005     * Configures the range axes.
1006     */

1007    public void configureRangeAxes() {
1008        for (int i = 0; i < this.rangeAxes.size(); i++) {
1009            ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1010            if (axis != null) {
1011                axis.configure();
1012            }
1013        }
1014    }
1015
1016    /**
1017     * Returns the location for a range axis. If this hasn't been set
1018     * explicitly, the method returns the location that is opposite to the
1019     * primary range axis location.
1020     *
1021     * @param index the axis index.
1022     *
1023     * @return The location (never <code>null</code>).
1024     */

1025    public AxisLocation getRangeAxisLocation(int index) {
1026        AxisLocation result = null;
1027        if (index < this.rangeAxisLocations.size()) {
1028            result = (AxisLocation) this.rangeAxisLocations.get(index);
1029        }
1030        if (result == null) {
1031            result = AxisLocation.getOpposite(getRangeAxisLocation());
1032        }
1033        return result;
1034    }
1035
1036    /**
1037     * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1038     * to all registered listeners.
1039     *
1040     * @param index the axis index.
1041     * @param location the location (<code>null</code> permitted).
1042     */

1043    public void setRangeAxisLocation(int index, AxisLocation location) {
1044        this.rangeAxisLocations.set(index, location);
1045        notifyListeners(new PlotChangeEvent(this));
1046    }
1047
1048    /**
1049     * Returns the edge for a range axis.
1050     *
1051     * @param index the axis index.
1052     *
1053     * @return The edge.
1054     */

1055    public RectangleEdge getRangeAxisEdge(int index) {
1056        AxisLocation location = getRangeAxisLocation(index);
1057        RectangleEdge result = Plot.resolveRangeAxisLocation(
1058            location, this.orientation
1059        );
1060        if (result == null) {
1061            result = RectangleEdge.opposite(getRangeAxisEdge());
1062        }
1063        return result;
1064    }
1065
1066    /**
1067     * Returns the primary dataset for the plot.
1068     *
1069     * @return The primary dataset (possibly <code>null</code>).
1070     */

1071    public XYDataset getDataset() {
1072        return getDataset(0);
1073    }
1074
1075    /**
1076     * Returns a dataset.
1077     *
1078     * @param index the dataset index.
1079     *
1080     * @return The dataset (possibly <code>null</code>).
1081     */

1082    public XYDataset getDataset(int index) {
1083        XYDataset result = null;
1084        if (this.datasets.size() > index) {
1085            result = (XYDataset) this.datasets.get(index);
1086        }
1087        return result;
1088    }
1089
1090    /**
1091     * Sets the primary dataset for the plot, replacing the existing dataset if
1092     * there is one.
1093     *
1094     * @param dataset the dataset (<code>null</code> permitted).
1095     */

1096    public void setDataset(XYDataset dataset) {
1097        setDataset(0, dataset);
1098    }
1099
1100    /**
1101     * Sets a dataset for the plot.
1102     *
1103     * @param index the dataset index.
1104     * @param dataset the dataset (<code>null</code> permitted).
1105     */

1106    public void setDataset(int index, XYDataset dataset) {
1107        XYDataset existing = getDataset(index);
1108        if (existing != null) {
1109            existing.removeChangeListener(this);
1110        }
1111        this.datasets.set(index, dataset);
1112        if (dataset != null) {
1113            dataset.addChangeListener(this);
1114        }
1115
1116        // send a dataset change event to self...
1117
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1118        datasetChanged(event);
1119    }
1120
1121    /**
1122     * Returns the number of datasets.
1123     *
1124     * @return The number of datasets.
1125     */

1126    public int getDatasetCount() {
1127        return this.datasets.size();
1128    }
1129
1130    /**
1131     * Returns the index of the specified dataset, or <code>-1</code> if the
1132     * dataset does not belong to the plot.
1133     *
1134     * @param dataset the dataset (<code>null</code> not permitted).
1135     *
1136     * @return The index.
1137     */

1138    public int indexOf(XYDataset dataset) {
1139        int result = -1;
1140        for (int i = 0; i < this.datasets.size(); i++) {
1141            if (dataset == this.datasets.get(i)) {
1142                result = i;
1143                break;
1144            }
1145        }
1146        return result;
1147    }
1148
1149    /**
1150     * Maps a dataset to a particular domain axis. All data will be plotted
1151     * against axis zero by default, no mapping is required for this case.
1152     *
1153     * @param index the dataset index (zero-based).
1154     * @param axisIndex the axis index.
1155     */

1156    public void mapDatasetToDomainAxis(int index, int axisIndex) {
1157        this.datasetToDomainAxisMap.put(
1158            new Integer JavaDoc(index), new Integer JavaDoc(axisIndex)
1159        );
1160        // fake a dataset change event to update axes...
1161
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1162    }
1163
1164    /**
1165     * Maps a dataset to a particular range axis. All data will be plotted
1166     * against axis zero by default, no mapping is required for this case.
1167     *
1168     * @param index the dataset index (zero-based).
1169     * @param axisIndex the axis index.
1170     */

1171    public void mapDatasetToRangeAxis(int index, int axisIndex) {
1172        this.datasetToRangeAxisMap.put(
1173            new Integer JavaDoc(index), new Integer JavaDoc(axisIndex)
1174        );
1175        // fake a dataset change event to update axes...
1176
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1177    }
1178
1179    /**
1180     * Returns the renderer for the primary dataset.
1181     *
1182     * @return The item renderer (possibly <code>null</code>).
1183     */

1184    public XYItemRenderer getRenderer() {
1185        return getRenderer(0);
1186    }
1187
1188    /**
1189     * Returns the renderer for a dataset, or <code>null</code>.
1190     *
1191     * @param index the renderer index.
1192     *
1193     * @return The renderer (possibly <code>null</code>).
1194     */

1195    public XYItemRenderer getRenderer(int index) {
1196        XYItemRenderer result = null;
1197        if (this.renderers.size() > index) {
1198            result = (XYItemRenderer) this.renderers.get(index);
1199        }
1200        return result;
1201
1202    }
1203
1204    /**
1205     * Sets the renderer for the primary dataset and sends a
1206     * {@link PlotChangeEvent} to all registered listeners. If the renderer
1207     * is set to <code>null</code>, no data will be displayed.
1208     *
1209     * @param renderer the renderer (<code>null</code> permitted).
1210     */

1211    public void setRenderer(XYItemRenderer renderer) {
1212        setRenderer(0, renderer);
1213    }
1214
1215    /**
1216     * Sets a renderer and sends a {@link PlotChangeEvent} is sent to all
1217     * registered listeners.
1218     *
1219     * @param index the index.
1220     * @param renderer the renderer.
1221     */

1222    public void setRenderer(int index, XYItemRenderer renderer) {
1223        XYItemRenderer existing = getRenderer(index);
1224        if (existing != null) {
1225            existing.removeChangeListener(this);
1226        }
1227        this.renderers.set(index, renderer);
1228        if (renderer != null) {
1229            renderer.setPlot(this);
1230            renderer.addChangeListener(this);
1231        }
1232        configureDomainAxes();
1233        configureRangeAxes();
1234        notifyListeners(new PlotChangeEvent(this));
1235    }
1236
1237    /**
1238     * Returns the dataset rendering order.
1239     *
1240     * @return The order (never <code>null</code>).
1241     */

1242    public DatasetRenderingOrder getDatasetRenderingOrder() {
1243        return this.datasetRenderingOrder;
1244    }
1245
1246    /**
1247     * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1248     * registered listeners. By default, the plot renders the primary dataset
1249     * last (so that the primary dataset overlays the secondary datasets).
1250     * You can reverse this if you want to.
1251     *
1252     * @param order the rendering order (<code>null</code> not permitted).
1253     */

1254    public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1255        if (order == null) {
1256            throw new IllegalArgumentException JavaDoc("Null 'order' argument.");
1257        }
1258        this.datasetRenderingOrder = order;
1259        notifyListeners(new PlotChangeEvent(this));
1260    }
1261
1262    /**
1263     * Returns the series rendering order.
1264     *
1265     * @return the order (never <code>null</code>).
1266     */

1267    public SeriesRenderingOrder getSeriesRenderingOrder() {
1268        return this.seriesRenderingOrder;
1269    }
1270
1271    /**
1272     * Sets the series order and sends a {@link PlotChangeEvent} to all
1273     * registered listeners. By default, the plot renders the primary series
1274     * last (so that the primary series appears to be on top).
1275     * You can reverse this if you want to.
1276     *
1277     * @param order the rendering order (<code>null</code> not permitted).
1278     */

1279    public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1280        if (order == null) {
1281            throw new IllegalArgumentException JavaDoc("Null 'order' argument.");
1282        }
1283        this.seriesRenderingOrder = order;
1284        notifyListeners(new PlotChangeEvent(this));
1285    }
1286
1287    /**
1288     * Returns the index of the specified renderer, or <code>-1</code> if the
1289     * renderer is not assigned to this plot.
1290     *
1291     * @param renderer the renderer (<code>null</code> permitted).
1292     *
1293     * @return The renderer index.
1294     */

1295    public int getIndexOf(XYItemRenderer renderer) {
1296        return this.renderers.indexOf(renderer);
1297    }
1298
1299    /**
1300     * Returns the renderer for the specified dataset. The code first
1301     * determines the index of the dataset, then checks if there is a
1302     * renderer with the same index (if not, the method returns renderer(0).
1303     *
1304     * @param dataset the dataset (<code>null</code> permitted).
1305     *
1306     * @return The renderer (possibly <code>null</code>).
1307     */

1308    public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1309        XYItemRenderer result = null;
1310        for (int i = 0; i < this.datasets.size(); i++) {
1311            if (this.datasets.get(i) == dataset) {
1312                result = (XYItemRenderer) this.renderers.get(i);
1313                if (result == null) {
1314                    result = getRenderer();
1315                }
1316                break;
1317            }
1318        }
1319        return result;
1320    }
1321
1322    /**
1323     * Returns the weight for this plot when it is used as a subplot within a
1324     * combined plot.
1325     *
1326     * @return The weight.
1327     */

1328    public int getWeight() {
1329        return this.weight;
1330    }
1331
1332    /**
1333     * Sets the weight for the plot.
1334     *
1335     * @param weight the weight.
1336     */

1337    public void setWeight(int weight) {
1338        this.weight = weight;
1339    }
1340
1341    /**
1342     * Returns <code>true</code> if the domain gridlines are visible, and
1343     * <code>false<code> otherwise.
1344     *
1345     * @return <code>true</code> or <code>false</code>.
1346     */

1347    public boolean isDomainGridlinesVisible() {
1348        return this.domainGridlinesVisible;
1349    }
1350
1351    /**
1352     * Sets the flag that controls whether or not the domain grid-lines are
1353     * visible.
1354     * <p>
1355     * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1356     * registered listeners.
1357     *
1358     * @param visible the new value of the flag.
1359     */

1360    public void setDomainGridlinesVisible(boolean visible) {
1361        if (this.domainGridlinesVisible != visible) {
1362            this.domainGridlinesVisible = visible;
1363            notifyListeners(new PlotChangeEvent(this));
1364        }
1365    }
1366
1367    /**
1368     * Returns the stroke for the grid-lines (if any) plotted against the
1369     * domain axis.
1370     *
1371     * @return The stroke.
1372     */

1373    public Stroke JavaDoc getDomainGridlineStroke() {
1374        return this.domainGridlineStroke;
1375    }
1376
1377    /**
1378     * Sets the stroke for the grid lines plotted against the domain axis.
1379     * <p>
1380     * If you set this to <code>null</code>, no grid lines will be drawn.
1381     *
1382     * @param stroke the stroke (<code>null</code> permitted).
1383     */

1384    public void setDomainGridlineStroke(Stroke JavaDoc stroke) {
1385        this.domainGridlineStroke = stroke;
1386        notifyListeners(new PlotChangeEvent(this));
1387    }
1388
1389    /**
1390     * Returns the paint for the grid lines (if any) plotted against the domain
1391     * axis.
1392     *
1393     * @return The paint.
1394     */

1395    public Paint JavaDoc getDomainGridlinePaint() {
1396        return this.domainGridlinePaint;
1397    }
1398
1399    /**
1400     * Sets the paint for the grid lines plotted against the domain axis.
1401     * <p>
1402     * If you set this to <code>null</code>, no grid lines will be drawn.
1403     *
1404     * @param paint the paint (<code>null</code> permitted).
1405     */

1406    public void setDomainGridlinePaint(Paint JavaDoc paint) {
1407        this.domainGridlinePaint = paint;
1408        notifyListeners(new PlotChangeEvent(this));
1409    }
1410
1411    /**
1412     * Returns <code>true</code> if the range axis grid is visible, and
1413     * <code>false<code> otherwise.
1414     *
1415     * @return A boolean.
1416     */

1417    public boolean isRangeGridlinesVisible() {
1418        return this.rangeGridlinesVisible;
1419    }
1420
1421    /**
1422     * Sets the flag that controls whether or not the range axis grid lines
1423     * are visible.
1424     * <p>
1425     * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1426     * registered listeners.
1427     *
1428     * @param visible the new value of the flag.
1429     */

1430    public void setRangeGridlinesVisible(boolean visible) {
1431        if (this.rangeGridlinesVisible != visible) {
1432            this.rangeGridlinesVisible = visible;
1433            notifyListeners(new PlotChangeEvent(this));
1434        }
1435    }
1436
1437    /**
1438     * Returns the stroke for the grid lines (if any) plotted against the
1439     * range axis.
1440     *
1441     * @return The stroke (never <code>null</code>).
1442     */

1443    public Stroke JavaDoc getRangeGridlineStroke() {
1444        return this.rangeGridlineStroke;
1445    }
1446
1447    /**
1448     * Sets the stroke for the grid lines plotted against the range axis,
1449     * and sends a {@link PlotChangeEvent} to all registered listeners.
1450     *
1451     * @param stroke the stroke (<code>null</code> not permitted).
1452     */

1453    public void setRangeGridlineStroke(Stroke JavaDoc stroke) {
1454        if (stroke == null) {
1455            throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
1456        }
1457        this.rangeGridlineStroke = stroke;
1458        notifyListeners(new PlotChangeEvent(this));
1459    }
1460
1461    /**
1462     * Returns the paint for the grid lines (if any) plotted against the range
1463     * axis.
1464     *
1465     * @return The paint (never <code>null</code>).
1466     */

1467    public Paint JavaDoc getRangeGridlinePaint() {
1468        return this.rangeGridlinePaint;
1469    }
1470
1471    /**
1472     * Sets the paint for the grid lines plotted against the range axis and
1473     * sends a {@link PlotChangeEvent} to all registered listeners.
1474     *
1475     * @param paint the paint (<code>null</code> permitted).
1476     */

1477    public void setRangeGridlinePaint(Paint JavaDoc paint) {
1478        this.rangeGridlinePaint = paint;
1479        notifyListeners(new PlotChangeEvent(this));
1480    }
1481
1482    /**
1483     * Returns a flag that controls whether or not a zero baseline is
1484     * displayed for the range axis.
1485     *
1486     * @return A boolean.
1487     */

1488    public boolean isRangeZeroBaselineVisible() {
1489        return this.rangeZeroBaselineVisible;
1490    }
1491
1492    /**
1493     * Sets the flag that controls whether or not the zero baseline is
1494     * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1495     * all registered listeners.
1496     *
1497     * @param visible the flag.
1498     */

1499    public void setRangeZeroBaselineVisible(boolean visible) {
1500        this.rangeZeroBaselineVisible = visible;
1501        notifyListeners(new PlotChangeEvent(this));
1502    }
1503
1504    /**
1505     * Returns the stroke used for the zero baseline against the range axis.
1506     *
1507     * @return The stroke (never <code>null</code>).
1508     */

1509    public Stroke JavaDoc getRangeZeroBaselineStroke() {
1510        return this.rangeZeroBaselineStroke;
1511    }
1512
1513    /**
1514     * Sets the stroke for the zero baseline for the range axis,
1515     * and sends a {@link PlotChangeEvent} to all registered listeners.
1516     *
1517     * @param stroke the stroke (<code>null</code> not permitted).
1518     */

1519    public void setRangeZeroBaselineStroke(Stroke JavaDoc stroke) {
1520        if (stroke == null) {
1521            throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
1522        }
1523        this.rangeZeroBaselineStroke = stroke;
1524        notifyListeners(new PlotChangeEvent(this));
1525    }
1526
1527    /**
1528     * Returns the paint for the zero baseline (if any) plotted against the
1529     * range axis.
1530     *
1531     * @return The paint (never <code>null</code>).
1532     */

1533    public Paint JavaDoc getRangeZeroBaselinePaint() {
1534        return this.rangeZeroBaselinePaint;
1535    }
1536
1537    /**
1538     * Sets the paint for the zero baseline plotted against the range axis and
1539     * sends a {@link PlotChangeEvent} to all registered listeners.
1540     *
1541     * @param paint the paint (<code>null</code> permitted).
1542     */

1543    public void setRangeZeroBaselinePaint(Paint JavaDoc paint) {
1544        this.rangeZeroBaselinePaint = paint;
1545        notifyListeners(new PlotChangeEvent(this));
1546    }
1547
1548    /**
1549     * Returns the paint used for the domain tick bands. If this is
1550     * <code>null</code>, no tick bands will be drawn.
1551     *
1552     * @return The paint (possibly <code>null</code>).
1553     */

1554    public Paint JavaDoc getDomainTickBandPaint() {
1555        return this.domainTickBandPaint;
1556    }
1557
1558    /**
1559     * Sets the paint for the domain tick bands.
1560     *
1561     * @param paint the paint (<code>null</code> permitted).
1562     */

1563    public void setDomainTickBandPaint(Paint JavaDoc paint) {
1564        this.domainTickBandPaint = paint;
1565        notifyListeners(new PlotChangeEvent(this));
1566    }
1567
1568    /**
1569     * Returns the paint used for the range tick bands. If this is
1570     * <code>null</code>, no tick bands will be drawn.
1571     *
1572     * @return The paint (possibly <code>null</code>).
1573     */

1574    public Paint JavaDoc getRangeTickBandPaint() {
1575        return this.rangeTickBandPaint;
1576    }
1577
1578    /**
1579     * Sets the paint for the range tick bands.
1580     *
1581     * @param paint the paint (<code>null</code> permitted).
1582     */

1583    public void setRangeTickBandPaint(Paint JavaDoc paint) {
1584        this.rangeTickBandPaint = paint;
1585        notifyListeners(new PlotChangeEvent(this));
1586    }
1587
1588    /**
1589     * Returns the origin for the quadrants that can be displayed on the plot.
1590     * This defaults to (0, 0).
1591     *
1592     * @return The origin point (never <code>null</code>).
1593     */

1594    public Point2D JavaDoc getQuadrantOrigin() {
1595        return this.quadrantOrigin;
1596    }
1597
1598    /**
1599     * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
1600     * registered listeners.
1601     *
1602     * @param origin the origin (<code>null</code> not permitted).
1603     */

1604    public void setQuadrantOrigin(Point2D JavaDoc origin) {
1605        if (origin == null) {
1606            throw new IllegalArgumentException JavaDoc("Null 'origin' argument.");
1607        }
1608        this.quadrantOrigin = origin;
1609        notifyListeners(new PlotChangeEvent(this));
1610    }
1611
1612    /**
1613     * Returns the paint used for the specified quadrant.
1614     *
1615     * @param index the quadrant index (0-3).
1616     *
1617     * @return The paint (possibly <code>null</code>).
1618     */

1619    public Paint JavaDoc getQuadrantPaint(int index) {
1620        if (index < 0 || index > 3) {
1621            throw new IllegalArgumentException JavaDoc(
1622                "The index should be in the range 0 to 3."
1623            );
1624        }
1625        return this.quadrantPaint[index];
1626    }
1627
1628    /**
1629     * Sets the paint used for the specified quadrant and sends a
1630     * {@link PlotChangeEvent} to all registered listeners.
1631     *
1632     * @param index the quadrant index (0-3).
1633     * @param paint the paint (<code>null</code> permitted).
1634     */

1635    public void setQuadrantPaint(int index, Paint JavaDoc paint) {
1636        if (index < 0 || index > 3) {
1637            throw new IllegalArgumentException JavaDoc(
1638                "The index should be in the range 0 to 3."
1639            );
1640        }
1641        this.quadrantPaint[index] = paint;
1642        notifyListeners(new PlotChangeEvent(this));
1643    }
1644
1645    /**
1646     * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
1647     * to all registered listeners.
1648     * <P>
1649     * Typically a marker will be drawn by the renderer as a line perpendicular
1650     * to the range axis, however this is entirely up to the renderer.
1651     *
1652     * @param marker the marker (<code>null</code> not permitted).
1653     */

1654    public void addDomainMarker(Marker marker) {
1655        // defer argument checking...
1656
addDomainMarker(marker, Layer.FOREGROUND);
1657    }
1658
1659    /**
1660     * Adds a marker for the domain axis in the specified layer and sends a
1661     * {@link PlotChangeEvent} to all registered listeners.
1662     * <P>
1663     * Typically a marker will be drawn by the renderer as a line perpendicular
1664     * to the range axis, however this is entirely up to the renderer.
1665     *
1666     * @param marker the marker (<code>null</code> not permitted).
1667     * @param layer the layer (foreground or background).
1668     */

1669    public void addDomainMarker(Marker marker, Layer layer) {
1670        addDomainMarker(0, marker, layer);
1671    }
1672
1673    /**
1674     * Clears all the (foreground and background) domain markers and sends a
1675     * {@link PlotChangeEvent} to all registered listeners.
1676     */

1677    public void clearDomainMarkers() {
1678        if (this.foregroundDomainMarkers != null) {
1679            this.foregroundDomainMarkers.clear();
1680        }
1681        if (this.backgroundDomainMarkers != null) {
1682            this.backgroundDomainMarkers.clear();
1683        }
1684        notifyListeners(new PlotChangeEvent(this));
1685    }
1686
1687    /**
1688     * Clears the (foreground and background) domain markers for a particular
1689     * renderer.
1690     *
1691     * @param index the renderer index.
1692     */

1693    public void clearDomainMarkers(int index) {
1694        Integer JavaDoc key = new Integer JavaDoc(index);
1695        if (this.backgroundDomainMarkers != null) {
1696            Collection JavaDoc markers
1697                = (Collection JavaDoc) this.backgroundDomainMarkers.get(key);
1698            if (markers != null) {
1699                markers.clear();
1700            }
1701        }
1702        if (this.foregroundRangeMarkers != null) {
1703            Collection JavaDoc markers
1704                = (Collection JavaDoc) this.foregroundDomainMarkers.get(key);
1705            if (markers != null) {
1706                markers.clear();
1707            }
1708        }
1709        notifyListeners(new PlotChangeEvent(this));
1710    }
1711
1712    /**
1713     * Adds a marker for a renderer and sends a {@link PlotChangeEvent} to
1714     * all registered listeners.
1715     * <P>
1716     * Typically a marker will be drawn by the renderer as a line perpendicular
1717     * to the domain axis (that the renderer is mapped to), however this is
1718     * entirely up to the renderer.
1719     *
1720     * @param index the renderer index.
1721     * @param marker the marker.
1722     * @param layer the layer (foreground or background).
1723     */

1724    public void addDomainMarker(int index, Marker marker, Layer layer) {
1725        Collection JavaDoc markers;
1726        if (layer == Layer.FOREGROUND) {
1727            markers = (Collection JavaDoc) this.foregroundDomainMarkers.get(
1728                new Integer JavaDoc(index)
1729            );
1730            if (markers == null) {
1731                markers = new java.util.ArrayList JavaDoc();
1732                this.foregroundDomainMarkers.put(new Integer JavaDoc(index), markers);
1733            }
1734            markers.add(marker);
1735        }
1736        else if (layer == Layer.BACKGROUND) {
1737            markers = (Collection JavaDoc) this.backgroundDomainMarkers.get(
1738                new Integer JavaDoc(index)
1739            );
1740            if (markers == null) {
1741                markers = new java.util.ArrayList JavaDoc();
1742                this.backgroundDomainMarkers.put(new Integer JavaDoc(index), markers);
1743            }
1744            markers.add(marker);
1745        }
1746        notifyListeners(new PlotChangeEvent(this));
1747    }
1748
1749    /**
1750     * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
1751     * all registered listeners.
1752     * <P>
1753     * Typically a marker will be drawn by the renderer as a line perpendicular
1754     * to the range axis, however this is entirely up to the renderer.
1755     *
1756     * @param marker the marker (<code>null</code> not permitted).
1757     */

1758    public void addRangeMarker(Marker marker) {
1759        addRangeMarker(marker, Layer.FOREGROUND);
1760    }
1761
1762    /**
1763     * Adds a marker for the range axis in the specified layer and sends a
1764     * {@link PlotChangeEvent} to all registered listeners.
1765     * <P>
1766     * Typically a marker will be drawn by the renderer as a line perpendicular
1767     * to the range axis, however this is entirely up to the renderer.
1768     *
1769     * @param marker the marker (<code>null</code> not permitted).
1770     * @param layer the layer (foreground or background).
1771     */

1772    public void addRangeMarker(Marker marker, Layer layer) {
1773        addRangeMarker(0, marker, layer);
1774    }
1775
1776    /**
1777     * Clears all the range markers and sends a {@link PlotChangeEvent} to all
1778     * registered listeners.
1779     */

1780    public void clearRangeMarkers() {
1781        if (this.foregroundRangeMarkers != null) {
1782            this.foregroundRangeMarkers.clear();
1783        }
1784        if (this.backgroundRangeMarkers != null) {
1785            this.backgroundRangeMarkers.clear();
1786        }
1787        notifyListeners(new PlotChangeEvent(this));
1788    }
1789
1790    /**
1791     * Adds a marker for a renderer and sends a {@link PlotChangeEvent} to
1792     * all registered listeners.
1793     * <P>
1794     * Typically a marker will be drawn by the renderer as a line perpendicular
1795     * to the range axis, however this is entirely up to the renderer.
1796     *
1797     * @param index the renderer index.
1798     * @param marker the marker.
1799     * @param layer the layer (foreground or background).
1800     */

1801    public void addRangeMarker(int index, Marker marker, Layer layer) {
1802        Collection JavaDoc markers;
1803        if (layer == Layer.FOREGROUND) {
1804            markers = (Collection JavaDoc) this.foregroundRangeMarkers.get(
1805                new Integer JavaDoc(index)
1806            );
1807            if (markers == null) {
1808                markers = new java.util.ArrayList JavaDoc();
1809                this.foregroundRangeMarkers.put(new Integer JavaDoc(index), markers);
1810            }
1811            markers.add(marker);
1812        }
1813        else if (layer == Layer.BACKGROUND) {
1814            markers = (Collection JavaDoc) this.backgroundRangeMarkers.get(
1815                new Integer JavaDoc(index)
1816            );
1817            if (markers == null) {
1818                markers = new java.util.ArrayList JavaDoc();
1819                this.backgroundRangeMarkers.put(new Integer JavaDoc(index), markers);
1820            }
1821            markers.add(marker);
1822        }
1823        notifyListeners(new PlotChangeEvent(this));
1824    }
1825
1826    /**
1827     * Clears the (foreground and background) range markers for a particular
1828     * renderer.
1829     *
1830     * @param index the renderer index.
1831     */

1832    public void clearRangeMarkers(int index) {
1833        Integer JavaDoc key = new Integer JavaDoc(index);
1834        if (this.backgroundRangeMarkers != null) {
1835            Collection JavaDoc markers
1836                = (Collection JavaDoc) this.backgroundRangeMarkers.get(key);
1837            if (markers != null) {
1838                markers.clear();
1839            }
1840        }
1841        if (this.foregroundRangeMarkers != null) {
1842            Collection JavaDoc markers
1843                = (Collection JavaDoc) this.foregroundRangeMarkers.get(key);
1844            if (markers != null) {
1845                markers.clear();
1846            }
1847        }
1848        notifyListeners(new PlotChangeEvent(this));
1849    }
1850
1851    /**
1852     * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
1853     * registered listeners.
1854     *
1855     * @param annotation the annotation (<code>null</code> not permitted).
1856     */

1857    public void addAnnotation(XYAnnotation annotation) {
1858        if (annotation == null) {
1859            throw new IllegalArgumentException JavaDoc("Null 'annotation' argument.");
1860        }
1861        this.annotations.add(annotation);
1862        notifyListeners(new PlotChangeEvent(this));
1863    }
1864
1865    /**
1866     * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
1867     * to all registered listeners.
1868     *
1869     * @param annotation the annotation (<code>null</code> not permitted).
1870     *
1871     * @return A boolean (indicates whether or not the annotation was removed).
1872     */

1873    public boolean removeAnnotation(XYAnnotation annotation) {
1874        if (annotation == null) {
1875            throw new IllegalArgumentException JavaDoc("Null 'annotation' argument.");
1876        }
1877        boolean removed = this.annotations.remove(annotation);
1878        if (removed) {
1879            notifyListeners(new PlotChangeEvent(this));
1880        }
1881        return removed;
1882    }
1883
1884    /**
1885     * Clears all the annotations and sends a {@link PlotChangeEvent} to all
1886     * registered listeners.
1887     */

1888    public void clearAnnotations() {
1889        this.annotations.clear();
1890        notifyListeners(new PlotChangeEvent(this));
1891    }
1892
1893    /**
1894     * Calculates the space required for all the axes in the plot.
1895     *
1896     * @param g2 the graphics device.
1897     * @param plotArea the plot area.
1898     *
1899     * @return The required space.
1900     */

1901    protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
1902                                           Rectangle2D JavaDoc plotArea) {
1903        AxisSpace space = new AxisSpace();
1904        space = calculateDomainAxisSpace(g2, plotArea, space);
1905        space = calculateRangeAxisSpace(g2, plotArea, space);
1906        return space;
1907    }
1908
1909    /**
1910     * Calculates the space required for the domain axis/axes.
1911     *
1912     * @param g2 the graphics device.
1913     * @param plotArea the plot area.
1914     * @param space a carrier for the result (<code>null</code> permitted).
1915     *
1916     * @return The required space.
1917     */

1918    protected AxisSpace calculateDomainAxisSpace(Graphics2D JavaDoc g2,
1919                                                 Rectangle2D JavaDoc plotArea,
1920                                                 AxisSpace space) {
1921
1922        if (space == null) {
1923            space = new AxisSpace();
1924        }
1925
1926        // reserve some space for the domain axis...
1927
if (this.fixedDomainAxisSpace != null) {
1928            if (this.orientation == PlotOrientation.HORIZONTAL) {
1929                space.ensureAtLeast(
1930                    this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT
1931                );
1932                space.ensureAtLeast(
1933                    this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT
1934                );
1935            }
1936            else if (this.orientation == PlotOrientation.VERTICAL) {
1937                space.ensureAtLeast(
1938                    this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP
1939                );
1940                space.ensureAtLeast(
1941                    this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM
1942                );
1943            }
1944        }
1945        else {
1946            // reserve space for the domain axes...
1947
for (int i = 0; i < this.domainAxes.size(); i++) {
1948                Axis axis = (Axis) this.domainAxes.get(i);
1949                if (axis != null) {
1950                    RectangleEdge edge = getDomainAxisEdge(i);
1951                    space = axis.reserveSpace(g2, this, plotArea, edge, space);
1952                }
1953            }
1954        }
1955
1956        return space;
1957
1958    }
1959
1960    /**
1961     * Calculates the space required for the range axis/axes.
1962     *
1963     * @param g2 the graphics device.
1964     * @param plotArea the plot area.
1965     * @param space a carrier for the result (<code>null</code> permitted).
1966     *
1967     * @return The required space.
1968     */

1969    protected AxisSpace calculateRangeAxisSpace(Graphics2D JavaDoc g2,
1970                                                Rectangle2D JavaDoc plotArea,
1971                                                AxisSpace space) {
1972
1973        if (space == null) {
1974            space = new AxisSpace();
1975        }
1976
1977        // reserve some space for the range axis...
1978
if (this.fixedRangeAxisSpace != null) {
1979            if (this.orientation == PlotOrientation.HORIZONTAL) {
1980                space.ensureAtLeast(
1981                    this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP
1982                );
1983                space.ensureAtLeast(
1984                    this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM
1985                );
1986            }
1987            else if (this.orientation == PlotOrientation.VERTICAL) {
1988                space.ensureAtLeast(
1989                    this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT
1990                );
1991                space.ensureAtLeast(
1992                    this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT
1993                );
1994            }
1995        }
1996        else {
1997            // reserve space for the range axes...
1998
for (int i = 0; i < this.rangeAxes.size(); i++) {
1999                Axis axis = (Axis) this.rangeAxes.get(i);
2000                if (axis != null) {
2001                    RectangleEdge edge = getRangeAxisEdge(i);
2002                    space = axis.reserveSpace(g2, this, plotArea, edge, space);
2003                }
2004            }
2005        }
2006        return space;
2007
2008    }
2009
2010    /**
2011     * Draws the plot within the specified area on a graphics device.
2012     *
2013     * @param g2 the graphics device.
2014     * @param area the plot area (in Java2D space).
2015     * @param anchor an anchor point in Java2D space (<code>null</code>
2016     * permitted).
2017     * @param parentState the state from the parent plot, if there is one
2018     * (<code>null</code> permitted).
2019     * @param info collects chart drawing information (<code>null</code>
2020     * permitted).
2021     */

2022    public void draw(Graphics2D JavaDoc g2,
2023                     Rectangle2D JavaDoc area,
2024                     Point2D JavaDoc anchor,
2025                     PlotState parentState,
2026                     PlotRenderingInfo info) {
2027
2028        // if the plot area is too small, just return...
2029
boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2030        boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2031        if (b1 || b2) {
2032            return;
2033        }
2034
2035        // record the plot area...
2036
if (info != null) {
2037            info.setPlotArea(area);
2038        }
2039
2040        // adjust the drawing area for the plot insets (if any)...
2041
RectangleInsets insets = getInsets();
2042        insets.trim(area);
2043
2044        AxisSpace space = calculateAxisSpace(g2, area);
2045        Rectangle2D JavaDoc dataArea = space.shrink(area, null);
2046        this.axisOffset.trim(dataArea);
2047
2048        if (info != null) {
2049            info.setDataArea(dataArea);
2050        }
2051
2052        // draw the plot background and axes...
2053
drawBackground(g2, dataArea);
2054        Map JavaDoc axisStateMap = drawAxes(g2, area, dataArea, info);
2055
2056        if (anchor != null && !dataArea.contains(anchor)) {
2057            anchor = null;
2058        }
2059        CrosshairState crosshairState = new CrosshairState();
2060        crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
2061        crosshairState.setAnchor(anchor);
2062        crosshairState.setCrosshairX(getDomainCrosshairValue());
2063        crosshairState.setCrosshairY(getRangeCrosshairValue());
2064        Shape JavaDoc originalClip = g2.getClip();
2065        Composite JavaDoc originalComposite = g2.getComposite();
2066
2067        g2.clip(dataArea);
2068        g2.setComposite(
2069            AlphaComposite.getInstance(
2070                AlphaComposite.SRC_OVER, getForegroundAlpha()
2071            )
2072        );
2073
2074        AxisState domainAxisState
2075            = (AxisState) axisStateMap.get(getDomainAxis());
2076        if (domainAxisState == null) {
2077            if (parentState != null) {
2078                domainAxisState
2079                    = (AxisState) parentState.getSharedAxisStates().get(
2080                        getDomainAxis()
2081                    );
2082            }
2083        }
2084        if (domainAxisState != null) {
2085            drawDomainTickBands(g2, dataArea, domainAxisState.getTicks());
2086            drawDomainGridlines(g2, dataArea, domainAxisState.getTicks());
2087        }
2088
2089        AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis());
2090        if (rangeAxisState == null) {
2091            if (parentState != null) {
2092                rangeAxisState
2093                    = (AxisState) parentState.getSharedAxisStates().get(
2094                        getRangeAxis()
2095                    );
2096            }
2097        }
2098        if (rangeAxisState != null) {
2099            drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks());
2100            drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2101            drawZeroRangeBaseline(g2, dataArea);
2102        }
2103
2104        // draw the markers that are associated with a specific renderer...
2105
for (int i = 0; i < this.renderers.size(); i++) {
2106            drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2107        }
2108        for (int i = 0; i < this.renderers.size(); i++) {
2109            drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2110        }
2111
2112        // now draw annotations and render data items...
2113
boolean foundData = false;
2114        DatasetRenderingOrder order = getDatasetRenderingOrder();
2115        if (order == DatasetRenderingOrder.FORWARD) {
2116
2117            // draw background annotations
2118
int rendererCount = this.renderers.size();
2119            for (int i = 0; i < rendererCount; i++) {
2120                XYItemRenderer r = getRenderer(i);
2121                if (r != null) {
2122                    ValueAxis domainAxis = getDomainAxisForDataset(i);
2123                    ValueAxis rangeAxis = getRangeAxisForDataset(i);
2124                    r.drawAnnotations(
2125                        g2, dataArea, domainAxis, rangeAxis,
2126                        Layer.BACKGROUND, info
2127                    );
2128                }
2129            }
2130
2131            // render data items...
2132
for (int i = 0; i < getDatasetCount(); i++) {
2133                foundData = render(g2, dataArea, i, info, crosshairState)
2134                    || foundData;
2135            }
2136
2137            // draw foreground annotations
2138
for (int i = 0; i < rendererCount; i++) {
2139                XYItemRenderer r = getRenderer(i);
2140                if (r != null) {
2141                    ValueAxis domainAxis = getDomainAxisForDataset(i);
2142                    ValueAxis rangeAxis = getRangeAxisForDataset(i);
2143                    r.drawAnnotations(
2144                        g2, dataArea, domainAxis, rangeAxis,
2145                        Layer.FOREGROUND, info
2146                    );
2147                }
2148            }
2149
2150        }
2151        else if (order == DatasetRenderingOrder.REVERSE) {
2152
2153            // draw background annotations
2154
int rendererCount = this.renderers.size();
2155            for (int i = rendererCount - 1; i >= 0; i--) {
2156                XYItemRenderer r = getRenderer(i);
2157                if (r != null) {
2158                    ValueAxis domainAxis = getDomainAxisForDataset(i);
2159                    ValueAxis rangeAxis = getRangeAxisForDataset(i);
2160                    r.drawAnnotations(
2161                        g2, dataArea, domainAxis, rangeAxis,
2162                        Layer.BACKGROUND, info
2163                    );
2164                }
2165            }
2166
2167            for (int i = getDatasetCount() - 1; i >= 0; i--) {
2168                foundData = render(g2, dataArea, i, info, crosshairState)
2169                    || foundData;
2170            }
2171
2172            // draw foreground annotations
2173
for (int i = rendererCount - 1; i >= 0; i--) {
2174                XYItemRenderer r = getRenderer(i);
2175                if (r != null) {
2176                    ValueAxis domainAxis = getDomainAxisForDataset(i);
2177                    ValueAxis rangeAxis = getRangeAxisForDataset(i);
2178                    r.drawAnnotations(
2179                        g2, dataArea, domainAxis, rangeAxis,
2180                        Layer.FOREGROUND, info
2181                    );
2182                }
2183            }
2184
2185        }
2186
2187        PlotOrientation orient = getOrientation();
2188
2189        // draw domain crosshair if required...
2190
if (!this.domainCrosshairLockedOnData && anchor != null) {
2191            double xx = getDomainAxis().java2DToValue(
2192                anchor.getX(), dataArea, getDomainAxisEdge()
2193            );
2194            crosshairState.setCrosshairX(xx);
2195        }
2196        setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
2197        if (isDomainCrosshairVisible()) {
2198            double x = getDomainCrosshairValue();
2199            Paint JavaDoc paint = getDomainCrosshairPaint();
2200            Stroke JavaDoc stroke = getDomainCrosshairStroke();
2201            if (orient == PlotOrientation.HORIZONTAL) {
2202                drawHorizontalLine(g2, dataArea, x, stroke, paint);
2203            }
2204            else if (orient == PlotOrientation.VERTICAL) {
2205                drawVerticalLine(g2, dataArea, x, stroke, paint);
2206            }
2207        }
2208
2209        // draw range crosshair if required...
2210
if (!this.rangeCrosshairLockedOnData && anchor != null) {
2211            double yy = getRangeAxis().java2DToValue(
2212                anchor.getY(), dataArea, getRangeAxisEdge()
2213            );
2214            crosshairState.setCrosshairX(yy);
2215        }
2216        setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
2217        if (isRangeCrosshairVisible()
2218            && getRangeAxis().getRange().contains(getRangeCrosshairValue())) {
2219            double y = getRangeCrosshairValue();
2220            Paint JavaDoc paint = getRangeCrosshairPaint();
2221            Stroke JavaDoc stroke = getRangeCrosshairStroke();
2222            if (orient == PlotOrientation.HORIZONTAL) {
2223                drawVerticalLine(g2, dataArea, y, stroke, paint);
2224            }
2225            else if (orient == PlotOrientation.VERTICAL) {
2226                drawHorizontalLine(g2, dataArea, y, stroke, paint);
2227            }
2228        }
2229
2230        if (!foundData) {
2231            drawNoDataMessage(g2, dataArea);
2232        }
2233
2234        for (int i = 0; i < this.renderers.size(); i++) {
2235            drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2236        }
2237        for (int i = 0; i < this.renderers.size(); i++) {
2238            drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2239        }
2240
2241        drawAnnotations(g2, dataArea, info);
2242        g2.setClip(originalClip);
2243        g2.setComposite(originalComposite);
2244
2245        drawOutline(g2, dataArea);
2246
2247    }
2248
2249    /**
2250     * Draws the background for the plot.
2251     *
2252     * @param g2 the graphics device.
2253     * @param area the area.
2254     */

2255    public void drawBackground(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area) {
2256        fillBackground(g2, area);
2257        drawQuadrants(g2, area);
2258        drawBackgroundImage(g2, area);
2259    }
2260
2261    /**
2262     * Draws the quadrants.
2263     *
2264     * @param g2 the graphics device.
2265     * @param area the area.
2266     */

2267    protected void drawQuadrants(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area) {
2268        // 0 | 1
2269
// --+--
2270
// 2 | 3
2271
boolean somethingToDraw = false;
2272
2273        ValueAxis xAxis = getDomainAxis();
2274        double x = this.quadrantOrigin.getX();
2275        double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge());
2276
2277        ValueAxis yAxis = getRangeAxis();
2278        double y = this.quadrantOrigin.getY();
2279        double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge());
2280
2281        double xmin = xAxis.getLowerBound();
2282        double xxmin = xAxis.valueToJava2D(xmin, area, getDomainAxisEdge());
2283
2284        double xmax = xAxis.getUpperBound();
2285        double xxmax = xAxis.valueToJava2D(xmax, area, getDomainAxisEdge());
2286
2287        double ymin = yAxis.getLowerBound();
2288        double yymin = yAxis.valueToJava2D(ymin, area, getRangeAxisEdge());
2289
2290        double ymax = yAxis.getUpperBound();
2291        double yymax = yAxis.valueToJava2D(ymax, area, getRangeAxisEdge());
2292
2293        Rectangle2D JavaDoc[] r = new Rectangle2D JavaDoc[] {null, null, null, null};
2294        if (this.quadrantPaint[0] != null) {
2295            if (x > xmin && y < ymax) {
2296                if (this.orientation == PlotOrientation.HORIZONTAL) {
2297                    r[0] = new Rectangle2D.Double JavaDoc(
2298                        Math.min(yymax, yy), Math.min(xxmin, xx),
2299                        Math.abs(yy - yymax), Math.abs(xx - xxmin)
2300                    );
2301                }
2302                else { // PlotOrientation.VERTICAL
2303
r[0] = new Rectangle2D.Double JavaDoc(
2304                        Math.min(xxmin, xx), Math.min(yymax, yy),
2305                        Math.abs(xx - xxmin), Math.abs(yy - yymax)
2306                    );
2307                }
2308                somethingToDraw = true;
2309            }
2310        }
2311        if (this.quadrantPaint[1] != null) {
2312            if (x < xmax && y < ymax) {
2313                if (this.orientation == PlotOrientation.HORIZONTAL) {
2314                    r[1] = new Rectangle2D.Double JavaDoc(
2315                        Math.min(yymax, yy), Math.min(xxmax, xx),
2316                        Math.abs(yy - yymax), Math.abs(xx - xxmax)
2317                    );
2318                }
2319                else { // PlotOrientation.VERTICAL
2320
r[1] = new Rectangle2D.Double JavaDoc(
2321                        Math.min(xx, xxmax), Math.min(yymax, yy),
2322                        Math.abs(xx - xxmax), Math.abs(yy - yymax)
2323                    );
2324                }
2325                somethingToDraw = true;
2326            }
2327        }
2328        if (this.quadrantPaint[2] != null) {
2329            if (x > xmin && y > ymin) {
2330                if (this.orientation == PlotOrientation.HORIZONTAL) {
2331                    r[2] = new Rectangle2D.Double JavaDoc(
2332                        Math.min(yymin, yy), Math.min(xxmin, xx),
2333                        Math.abs(yy - yymin), Math.abs(xx - xxmin)
2334                    );
2335                }
2336                else { // PlotOrientation.VERTICAL
2337
r[2] = new Rectangle2D.Double JavaDoc(
2338                        Math.min(xxmin, xx), Math.min(yymin, yy),
2339                        Math.abs(xx - xxmin), Math.abs(yy - yymin)
2340                    );
2341                }
2342                somethingToDraw = true;
2343            }
2344        }
2345        if (this.quadrantPaint[3] != null) {
2346            if (x < xmax && y > ymin) {
2347                if (this.orientation == PlotOrientation.HORIZONTAL) {
2348                    r[3] = new Rectangle2D.Double JavaDoc(
2349                        Math.min(yymin, yy), Math.min(xxmax, xx),
2350                        Math.abs(yy - yymin), Math.abs(xx - xxmax)
2351                    );
2352                }
2353                else { // PlotOrientation.VERTICAL
2354
r[3] = new Rectangle2D.Double JavaDoc(
2355                        Math.min(xx, xxmax), Math.min(yymin, yy),
2356                        Math.abs(xx - xxmax), Math.abs(yy - yymin)
2357                    );
2358                }
2359                somethingToDraw = true;
2360            }
2361        }
2362        if (somethingToDraw) {
2363            Composite JavaDoc originalComposite = g2.getComposite();
2364            g2.setComposite(
2365                AlphaComposite.getInstance(
2366                    AlphaComposite.SRC_OVER, getBackgroundAlpha()
2367                )
2368            );
2369            for (int i = 0; i < 4; i++) {
2370                if (this.quadrantPaint[i] != null && r[i] != null) {
2371                    g2.setPaint(this.quadrantPaint[i]);
2372                    g2.fill(r[i]);
2373                }
2374            }
2375            g2.setComposite(originalComposite);
2376        }
2377    }
2378
2379    /**
2380     * Draws the domain tick bands, if any.
2381     *
2382     * @param g2 the graphics device.
2383     * @param dataArea the data area.
2384     * @param ticks the ticks.
2385     */

2386    public void drawDomainTickBands(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2387                                    List JavaDoc ticks) {
2388        // draw the domain tick bands, if any...
2389
Paint JavaDoc bandPaint = getDomainTickBandPaint();
2390        if (bandPaint != null) {
2391            boolean fillBand = false;
2392            ValueAxis xAxis = getDomainAxis();
2393            double previous = xAxis.getLowerBound();
2394            Iterator JavaDoc iterator = ticks.iterator();
2395            while (iterator.hasNext()) {
2396                ValueTick tick = (ValueTick) iterator.next();
2397                double current = tick.getValue();
2398                if (fillBand) {
2399                    getRenderer().fillDomainGridBand(
2400                        g2, this, xAxis, dataArea, previous, current
2401                    );
2402                }
2403                previous = current;
2404                fillBand = !fillBand;
2405            }
2406            double end = xAxis.getUpperBound();
2407            if (fillBand) {
2408                getRenderer().fillDomainGridBand(
2409                    g2, this, xAxis, dataArea, previous, end
2410                );
2411            }
2412        }
2413    }
2414
2415    /**
2416     * Draws the range tick bands, if any.
2417     *
2418     * @param g2 the graphics device.
2419     * @param dataArea the data area.
2420     * @param ticks the ticks.
2421     */

2422    public void drawRangeTickBands(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2423                                   List JavaDoc ticks) {
2424
2425        // draw the range tick bands, if any...
2426
Paint JavaDoc bandPaint = getRangeTickBandPaint();
2427        if (bandPaint != null) {
2428            boolean fillBand = false;
2429            ValueAxis axis = getRangeAxis();
2430            double previous = axis.getLowerBound();
2431            Iterator JavaDoc iterator = ticks.iterator();
2432            while (iterator.hasNext()) {
2433                ValueTick tick = (ValueTick) iterator.next();
2434                double current = tick.getValue();
2435                if (fillBand) {
2436                    getRenderer().fillRangeGridBand(
2437                        g2, this, axis, dataArea, previous, current
2438                    );
2439                }
2440                previous = current;
2441                fillBand = !fillBand;
2442            }
2443            double end = axis.getUpperBound();
2444            if (fillBand) {
2445                getRenderer().fillRangeGridBand(
2446                    g2, this, axis, dataArea, previous, end
2447                );
2448            }
2449        }
2450    }
2451
2452    /**
2453     * A utility method for drawing the axes.
2454     *
2455     * @param g2 the graphics device (<code>null</code> not permitted).
2456     * @param plotArea the plot area (<code>null</code> not permitted).
2457     * @param dataArea the data area (<code>null</code> not permitted).
2458     * @param plotState collects information about the plot (<code>null</code>
2459     * permitted).
2460     *
2461     * @return A map containing the state for each axis drawn.
2462     */

2463    protected Map JavaDoc drawAxes(Graphics2D JavaDoc g2,
2464                           Rectangle2D JavaDoc plotArea,
2465                           Rectangle2D JavaDoc dataArea,
2466                           PlotRenderingInfo plotState) {
2467
2468        AxisCollection axisCollection = new AxisCollection();
2469
2470        // add domain axes to lists...
2471
for (int index = 0; index < this.domainAxes.size(); index++) {
2472            ValueAxis axis = (ValueAxis) this.domainAxes.get(index);
2473            if (axis != null) {
2474                axisCollection.add(axis, getDomainAxisEdge(index));
2475            }
2476        }
2477
2478        // add range axes to lists...
2479
for (int index = 0; index < this.rangeAxes.size(); index++) {
2480            ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index);
2481            if (yAxis != null) {
2482                axisCollection.add(yAxis, getRangeAxisEdge(index));
2483            }
2484        }
2485
2486        Map JavaDoc axisStateMap = new HashMap JavaDoc();
2487
2488        // draw the top axes
2489
double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(
2490            dataArea.getHeight()
2491        );
2492        Iterator JavaDoc iterator = axisCollection.getAxesAtTop().iterator();
2493        while (iterator.hasNext()) {
2494            ValueAxis axis = (ValueAxis) iterator.next();
2495            AxisState info = axis.draw(
2496                g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState
2497            );
2498            cursor = info.getCursor();
2499            axisStateMap.put(axis, info);
2500        }
2501
2502        // draw the bottom axes
2503
cursor = dataArea.getMaxY()
2504                 + this.axisOffset.calculateBottomOutset(dataArea.getHeight());
2505        iterator = axisCollection.getAxesAtBottom().iterator();
2506        while (iterator.hasNext()) {
2507            ValueAxis axis = (ValueAxis) iterator.next();
2508            AxisState info = axis.draw(
2509                g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM, plotState
2510            );
2511            cursor = info.getCursor();
2512            axisStateMap.put(axis, info);
2513        }
2514
2515        // draw the left axes
2516
cursor = dataArea.getMinX()
2517                 - this.axisOffset.calculateLeftOutset(dataArea.getWidth());
2518        iterator = axisCollection.getAxesAtLeft().iterator();
2519        while (iterator.hasNext()) {
2520            ValueAxis axis = (ValueAxis) iterator.next();
2521            AxisState info = axis.draw(
2522                g2, cursor, plotArea, dataArea, RectangleEdge.LEFT, plotState
2523             );
2524            cursor = info.getCursor();
2525            axisStateMap.put(axis, info);
2526        }
2527
2528        // draw the right axes
2529
cursor = dataArea.getMaxX()
2530                 + this.axisOffset.calculateRightOutset(dataArea.getWidth());
2531        iterator = axisCollection.getAxesAtRight().iterator();
2532        while (iterator.hasNext()) {
2533            ValueAxis axis = (ValueAxis) iterator.next();
2534            AxisState info = axis.draw(
2535                g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT, plotState
2536            );
2537            cursor = info.getCursor();
2538            axisStateMap.put(axis, info);
2539        }
2540
2541        return axisStateMap;
2542    }
2543
2544    /**
2545     * Draws a representation of the data within the dataArea region, using the
2546     * current renderer.
2547     * <P>
2548     * The <code>info</code> and <code>crosshairState</code> arguments may be
2549     * <code>null</code>.
2550     *
2551     * @param g2 the graphics device.
2552     * @param dataArea the region in which the data is to be drawn.
2553     * @param index the dataset index.
2554     * @param info an optional object for collection dimension information.
2555     * @param crosshairState collects crosshair information
2556     * (<code>null</code> permitted).
2557     *
2558     * @return A flag that indicates whether any data was actually rendered.
2559     */

2560    public boolean render(Graphics2D JavaDoc g2,
2561                          Rectangle2D JavaDoc dataArea,
2562                          int index,
2563                          PlotRenderingInfo info,
2564                          CrosshairState crosshairState) {
2565
2566        boolean foundData = false;
2567        XYDataset dataset = getDataset(index);
2568        if (!DatasetUtilities.isEmptyOrNull(dataset)) {
2569            foundData = true;
2570            ValueAxis xAxis = getDomainAxisForDataset(index);
2571            ValueAxis yAxis = getRangeAxisForDataset(index);
2572            XYItemRenderer renderer = getRenderer(index);
2573            if (renderer == null) {
2574                renderer = getRenderer();
2575            }
2576
2577            XYItemRendererState state = renderer.initialise(
2578                g2, dataArea, this, dataset, info
2579            );
2580            int passCount = renderer.getPassCount();
2581
2582            SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
2583            if (seriesOrder == SeriesRenderingOrder.REVERSE) {
2584                   //render series in reverse order
2585
for (int pass = 0; pass < passCount; pass++) {
2586                    int seriesCount = dataset.getSeriesCount();
2587                    for (int series = seriesCount-1; series >= 0 ; series--) {
2588                        int itemCount = dataset.getItemCount(series);
2589                        for (int item = 0; item < itemCount; item++) {
2590                            renderer.drawItem(
2591                                g2, state, dataArea, info,
2592                                this, xAxis, yAxis, dataset, series, item,
2593                                crosshairState, pass
2594                            );
2595                        }
2596                    }
2597                }
2598            }
2599            else {
2600                   //render series in forward order
2601
for (int pass = 0; pass < passCount; pass++) {
2602                    int seriesCount = dataset.getSeriesCount();
2603                    for (int series = 0; series < seriesCount; series++) {
2604                        int itemCount = dataset.getItemCount(series);
2605                        for (int item = 0; item < itemCount; item++) {
2606                            renderer.drawItem(
2607                                g2, state, dataArea, info,
2608                                this, xAxis, yAxis, dataset, series, item,
2609                                crosshairState, pass
2610                            );
2611                        }
2612                    }
2613                }
2614            }
2615        }
2616        return foundData;
2617    }
2618
2619    /**
2620     * Returns the domain axis for a dataset.
2621     *
2622     * @param index the dataset index.
2623     *
2624     * @return The axis.
2625     */

2626    public ValueAxis getDomainAxisForDataset(int index) {
2627
2628        if (index < 0 || index >= getDatasetCount()) {
2629            throw new IllegalArgumentException JavaDoc("Index 'index' out of bounds.");
2630        }
2631
2632        ValueAxis valueAxis = null;
2633        Integer JavaDoc axisIndex = (Integer JavaDoc) this.datasetToDomainAxisMap.get(
2634            new Integer JavaDoc(index)
2635        );
2636        if (axisIndex != null) {
2637            valueAxis = getDomainAxis(axisIndex.intValue());
2638        }
2639        else {
2640            valueAxis = getDomainAxis(0);
2641        }
2642        return valueAxis;
2643
2644    }
2645
2646    /**
2647     * Returns the range axis for a dataset.
2648     *
2649     * @param index the dataset index.
2650     *
2651     * @return The axis.
2652     */

2653    public ValueAxis getRangeAxisForDataset(int index) {
2654
2655        if (index < 0 || index >= getDatasetCount()) {
2656            throw new IllegalArgumentException JavaDoc("Index 'index' out of bounds.");
2657        }
2658
2659        ValueAxis valueAxis = null;
2660        Integer JavaDoc axisIndex
2661            = (Integer JavaDoc) this.datasetToRangeAxisMap.get(new Integer JavaDoc(index));
2662        if (axisIndex != null) {
2663            valueAxis = getRangeAxis(axisIndex.intValue());
2664        }
2665        else {
2666            valueAxis = getRangeAxis(0);
2667        }
2668        return valueAxis;
2669
2670    }
2671
2672    /**
2673     * Draws the gridlines for the plot, if they are visible.
2674     *
2675     * @param g2 the graphics device.
2676     * @param dataArea the data area.
2677     * @param ticks the ticks.
2678     */

2679    protected void drawDomainGridlines(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2680                                       List JavaDoc ticks) {
2681
2682        // no renderer, no gridlines...
2683
if (getRenderer() == null) {
2684            return;
2685        }
2686
2687        // draw the domain grid lines, if any...
2688
if (isDomainGridlinesVisible()) {
2689            Stroke JavaDoc gridStroke = getDomainGridlineStroke();
2690            Paint JavaDoc gridPaint = getDomainGridlinePaint();
2691            if ((gridStroke != null) && (gridPaint != null)) {
2692                Iterator JavaDoc iterator = ticks.iterator();
2693                while (iterator.hasNext()) {
2694                    ValueTick tick = (ValueTick) iterator.next();
2695                    getRenderer().drawDomainGridLine(
2696                        g2, this, getDomainAxis(), dataArea, tick.getValue()
2697                    );
2698                }
2699            }
2700        }
2701    }
2702
2703    /**
2704     * Draws the gridlines for the plot's primary range axis, if they are
2705     * visible.
2706     *
2707     * @param g2 the graphics device.
2708     * @param area the data area.
2709     * @param ticks the ticks.
2710     */

2711    protected void drawRangeGridlines(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area,
2712                                      List JavaDoc ticks) {
2713
2714        // draw the range grid lines, if any...
2715
if (isRangeGridlinesVisible()) {
2716            Stroke JavaDoc gridStroke = getRangeGridlineStroke();
2717            Paint JavaDoc gridPaint = getRangeGridlinePaint();
2718            ValueAxis axis = getRangeAxis();
2719            if (axis != null) {
2720                Iterator JavaDoc iterator = ticks.iterator();
2721                while (iterator.hasNext()) {
2722                    ValueTick tick = (ValueTick) iterator.next();
2723                    if (tick.getValue() != 0.0
2724                            || !isRangeZeroBaselineVisible()) {
2725                        getRenderer().drawRangeLine(
2726                            g2, this, getRangeAxis(), area,
2727                            tick.getValue(), gridPaint, gridStroke
2728                        );
2729                    }
2730                }
2731            }
2732        }
2733    }
2734
2735    /**
2736     * Draws a base line across the chart at value zero on the range axis.
2737     *
2738     * @param g2 the graphics device.
2739     * @param area the data area.
2740     */

2741    protected void drawZeroRangeBaseline(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area) {
2742        if (isRangeZeroBaselineVisible()) {
2743            getRenderer().drawRangeLine(
2744                g2, this, getRangeAxis(), area,
2745                0.0, this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke
2746            );
2747        }
2748    }
2749
2750    /**
2751     * Draws the annotations for the plot.
2752     *
2753     * @param g2 the graphics device.
2754     * @param dataArea the data area.
2755     * @param info the chart rendering info.
2756     */

2757    public void drawAnnotations(Graphics2D JavaDoc g2,
2758                                Rectangle2D JavaDoc dataArea,
2759                                PlotRenderingInfo info) {
2760
2761        Iterator JavaDoc iterator = this.annotations.iterator();
2762        while (iterator.hasNext()) {
2763            XYAnnotation annotation = (XYAnnotation) iterator.next();
2764            ValueAxis xAxis = getDomainAxis();
2765            ValueAxis yAxis = getRangeAxis();
2766            annotation.draw(g2, this, dataArea, xAxis, yAxis, 0, info);
2767        }
2768
2769    }
2770
2771    /**
2772     * Draws the domain markers (if any) for an axis and layer. This method is
2773     * typically called from within the draw() method.
2774     *
2775     * @param g2 the graphics device.
2776     * @param dataArea the data area.
2777     * @param index the renderer index.
2778     * @param layer the layer (foreground or background).
2779     */

2780    protected void drawDomainMarkers(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2781                                     int index, Layer layer) {
2782
2783        XYItemRenderer r = getRenderer(index);
2784        if (r == null) {
2785            return;
2786        }
2787
2788        Collection JavaDoc markers = getDomainMarkers(index, layer);
2789        ValueAxis axis = getDomainAxisForDataset(index);
2790        if (markers != null && axis != null) {
2791            Iterator JavaDoc iterator = markers.iterator();
2792            while (iterator.hasNext()) {
2793                Marker marker = (Marker) iterator.next();
2794                r.drawDomainMarker(g2, this, axis, marker, dataArea);
2795            }
2796        }
2797
2798    }
2799
2800    /**
2801     * Draws the range markers (if any) for a renderer and layer. This method
2802     * is typically called from within the draw() method.
2803     *
2804     * @param g2 the graphics device.
2805     * @param dataArea the data area.
2806     * @param index the renderer index.
2807     * @param layer the layer (foreground or background).
2808     */

2809    protected void drawRangeMarkers(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2810                                    int index, Layer layer) {
2811
2812        XYItemRenderer r = getRenderer(index);
2813        if (r == null) {
2814            return;
2815        }
2816
2817        Collection JavaDoc markers = getRangeMarkers(index, layer);
2818        ValueAxis axis = getRangeAxis(index);
2819        // TODO: get the axis that the renderer maps to
2820
if (markers != null && axis != null) {
2821            Iterator JavaDoc iterator = markers.iterator();
2822            while (iterator.hasNext()) {
2823                Marker marker = (Marker) iterator.next();
2824                r.drawRangeMarker(g2, this, axis, marker, dataArea);
2825            }
2826        }
2827
2828    }
2829
2830    /**
2831     * Returns the list of domain markers (read only) for the specified layer.
2832     *
2833     * @param layer the layer (foreground or background).
2834     *
2835     * @return The list of domain markers.
2836     */

2837    public Collection JavaDoc getDomainMarkers(Layer layer) {
2838        return getDomainMarkers(0, layer);
2839    }
2840
2841    /**
2842     * Returns the list of range markers (read only) for the specified layer.
2843     *
2844     * @param layer the layer (foreground or background).
2845     *
2846     * @return The list of range markers.
2847     */

2848    public Collection JavaDoc getRangeMarkers(Layer layer) {
2849        return getRangeMarkers(0, layer);
2850    }
2851
2852    /**
2853     * Returns a collection of domain markers for a particular renderer and
2854     * layer.
2855     *
2856     * @param index the renderer index.
2857     * @param layer the layer.
2858     *
2859     * @return A collection of markers (possibly <code>null</code>).
2860     */

2861    public Collection JavaDoc getDomainMarkers(int index, Layer layer) {
2862        Collection JavaDoc result = null;
2863        Integer JavaDoc key = new Integer JavaDoc(index);
2864        if (layer == Layer.FOREGROUND) {
2865            result = (Collection JavaDoc) this.foregroundDomainMarkers.get(key);
2866        }
2867        else if (layer == Layer.BACKGROUND) {
2868            result = (Collection JavaDoc) this.backgroundDomainMarkers.get(key);
2869        }
2870        if (result != null) {
2871            result = Collections.unmodifiableCollection(result);
2872        }
2873        return result;
2874    }
2875
2876    /**
2877     * Returns a collection of range markers for a particular renderer and
2878     * layer.
2879     *
2880     * @param index the renderer index.
2881     * @param layer the layer.
2882     *
2883     * @return A collection of markers (possibly <code>null</code>).
2884     */

2885    public Collection JavaDoc getRangeMarkers(int index, Layer layer) {
2886        Collection JavaDoc result = null;
2887        Integer JavaDoc key = new Integer JavaDoc(index);
2888        if (layer == Layer.FOREGROUND) {
2889            result = (Collection JavaDoc) this.foregroundRangeMarkers.get(key);
2890        }
2891        else if (layer == Layer.BACKGROUND) {
2892            result = (Collection JavaDoc) this.backgroundRangeMarkers.get(key);
2893        }
2894        if (result != null) {
2895            result = Collections.unmodifiableCollection(result);
2896        }
2897        return result;
2898    }
2899
2900    /**
2901     * Utility method for drawing a horizontal line across the data area of the
2902     * plot.
2903     *
2904     * @param g2 the graphics device.
2905     * @param dataArea the data area.
2906     * @param value the coordinate, where to draw the line.
2907     * @param stroke the stroke to use.
2908     * @param paint the paint to use.
2909     */

2910    protected void drawHorizontalLine(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2911                                      double value, Stroke JavaDoc stroke,
2912                                      Paint JavaDoc paint) {
2913
2914        ValueAxis axis = getRangeAxis();
2915        if (getOrientation() == PlotOrientation.HORIZONTAL) {
2916            axis = getDomainAxis();
2917        }
2918        if (axis.getRange().contains(value)) {
2919            double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT);
2920            Line2D JavaDoc line = new Line2D.Double JavaDoc(
2921                dataArea.getMinX(), yy, dataArea.getMaxX(), yy
2922            );
2923            g2.setStroke(stroke);
2924            g2.setPaint(paint);
2925            g2.draw(line);
2926        }
2927
2928    }
2929
2930    /**
2931     * Utility method for drawing a vertical line on the data area of the plot.
2932     *
2933     * @param g2 the graphics device.
2934     * @param dataArea the data area.
2935     * @param value the coordinate, where to draw the line.
2936     * @param stroke the stroke to use.
2937     * @param paint the paint to use.
2938     */

2939    protected void drawVerticalLine(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2940                                    double value, Stroke JavaDoc stroke, Paint JavaDoc paint) {
2941
2942        ValueAxis axis = getDomainAxis();
2943        if (getOrientation() == PlotOrientation.HORIZONTAL) {
2944            axis = getRangeAxis();
2945        }
2946        if (axis.getRange().contains(value)) {
2947            double xx = axis.valueToJava2D(
2948                value, dataArea, RectangleEdge.BOTTOM
2949            );
2950            Line2D JavaDoc line = new Line2D.Double JavaDoc(
2951                xx, dataArea.getMinY(), xx, dataArea.getMaxY()
2952            );
2953            g2.setStroke(stroke);
2954            g2.setPaint(paint);
2955            g2.draw(line);
2956        }
2957
2958    }
2959
2960    /**
2961     * Handles a 'click' on the plot by updating the anchor values...
2962     *
2963     * @param x the x-coordinate, where the click occurred, in Java2D space.
2964     * @param y the y-coordinate, where the click occurred, in Java2D space.
2965     * @param info object containing information about the plot dimensions.
2966     */

2967    public void handleClick(int x, int y, PlotRenderingInfo info) {
2968
2969        Rectangle2D JavaDoc dataArea = info.getDataArea();
2970        if (dataArea.contains(x, y)) {
2971            // set the anchor value for the horizontal axis...
2972
ValueAxis da = getDomainAxis();
2973            if (da != null) {
2974                double hvalue = da.java2DToValue(
2975                    x, info.getDataArea(), getDomainAxisEdge()
2976                );
2977
2978                setDomainCrosshairValue(hvalue);
2979            }
2980
2981            // set the anchor value for the vertical axis...
2982
ValueAxis ra = getRangeAxis();
2983            if (ra != null) {
2984                double vvalue = ra.java2DToValue(
2985                    y, info.getDataArea(), getRangeAxisEdge()
2986                );
2987                setRangeCrosshairValue(vvalue);
2988            }
2989        }
2990    }
2991
2992    /**
2993     * A utility method that returns a list of datasets that are mapped to a
2994     * particular axis.
2995     *
2996     * @param axisIndex the axis index (<code>null</code> not permitted).
2997     *
2998     * @return A list of datasets.
2999     */

3000    private List JavaDoc getDatasetsMappedToDomainAxis(Integer JavaDoc axisIndex) {
3001        if (axisIndex == null) {
3002            throw new IllegalArgumentException JavaDoc("Null 'axisIndex' argument.");
3003        }
3004        List JavaDoc result = new ArrayList JavaDoc();
3005        for (int i = 0; i < this.datasets.size(); i++) {
3006            Integer JavaDoc mappedAxis = (Integer JavaDoc) this.datasetToDomainAxisMap.get(
3007                new Integer JavaDoc(i)
3008            );
3009            if (mappedAxis == null) {
3010                if (axisIndex.equals(ZERO)) {
3011                    result.add(this.datasets.get(i));
3012                }
3013            }
3014            else {
3015                if (mappedAxis.equals(axisIndex)) {
3016                    result.add(this.datasets.get(i));
3017                }
3018            }
3019        }
3020        return result;
3021    }
3022
3023    /**
3024     * A utility method that returns a list of datasets that are mapped to a
3025     * particular axis.
3026     *
3027     * @param axisIndex the axis index (<code>null</code> not permitted).
3028     *
3029     * @return A list of datasets.
3030     */

3031    private List JavaDoc getDatasetsMappedToRangeAxis(Integer JavaDoc axisIndex) {
3032        if (axisIndex == null) {
3033            throw new IllegalArgumentException JavaDoc("Null 'axisIndex' argument.");
3034        }
3035        List JavaDoc result = new ArrayList JavaDoc();
3036        for (int i = 0; i < this.datasets.size(); i++) {
3037            Integer JavaDoc mappedAxis = (Integer JavaDoc) this.datasetToRangeAxisMap.get(
3038                new Integer JavaDoc(i)
3039            );
3040            if (mappedAxis == null) {
3041                if (axisIndex.equals(ZERO)) {
3042                    result.add(this.datasets.get(i));
3043                }
3044            }
3045            else {
3046                if (mappedAxis.equals(axisIndex)) {
3047                    result.add(this.datasets.get(i));
3048                }
3049            }
3050        }
3051        return result;
3052    }
3053
3054    /**
3055     * Returns the index of the given domain axis.
3056     *
3057     * @param axis the axis.
3058     *
3059     * @return The axis index.
3060     */

3061    protected int getDomainAxisIndex(ValueAxis axis) {
3062        int result = this.domainAxes.indexOf(axis);
3063        if (result < 0) {
3064            // try the parent plot
3065
Plot parent = getParent();
3066            if (parent instanceof XYPlot) {
3067                XYPlot p = (XYPlot) parent;
3068                result = p.getDomainAxisIndex(axis);
3069            }
3070        }
3071        return result;
3072    }
3073
3074    /**
3075     * Returns the index of the given range axis.
3076     *
3077     * @param axis the axis.
3078     *
3079     * @return The axis index.
3080     */

3081    protected int getRangeAxisIndex(ValueAxis axis) {
3082        int result = this.rangeAxes.indexOf(axis);
3083        if (result < 0) {
3084            // try the parent plot
3085
Plot parent = getParent();
3086            if (parent instanceof XYPlot) {
3087                XYPlot p = (XYPlot) parent;
3088                result = p.getRangeAxisIndex(axis);
3089            }
3090        }
3091        return result;
3092    }
3093
3094    /**
3095     * Returns the range for the specified axis.
3096     *
3097     * @param axis the axis.
3098     *
3099     * @return The range.
3100     */

3101    public Range getDataRange(ValueAxis axis) {
3102
3103        Range result = null;
3104        List JavaDoc mappedDatasets = new ArrayList JavaDoc();
3105        boolean isDomainAxis = true;
3106
3107        // is it a domain axis?
3108
int domainIndex = getDomainAxisIndex(axis);
3109        if (domainIndex >= 0) {
3110            isDomainAxis = true;
3111            mappedDatasets.addAll(
3112                getDatasetsMappedToDomainAxis(new Integer JavaDoc(domainIndex))
3113            );
3114        }
3115
3116        // or is it a range axis?
3117
int rangeIndex = getRangeAxisIndex(axis);
3118        if (rangeIndex >= 0) {
3119            isDomainAxis = false;
3120            mappedDatasets.addAll(
3121                getDatasetsMappedToRangeAxis(new Integer JavaDoc(rangeIndex))
3122            );
3123        }
3124
3125        // iterate through the datasets that map to the axis and get the union
3126
// of the ranges.
3127
Iterator JavaDoc iterator = mappedDatasets.iterator();
3128        while (iterator.hasNext()) {
3129            XYDataset d = (XYDataset) iterator.next();
3130            if (d != null) {
3131                XYItemRenderer r = getRendererForDataset(d);
3132                if (isDomainAxis) {
3133                    if (r != null) {
3134                        result = Range.combine(result, r.findDomainBounds(d));
3135                    }
3136                    else {
3137                        result = Range.combine(
3138                            result, DatasetUtilities.findDomainBounds(d)
3139                        );
3140                    }
3141                }
3142                else {
3143                    if (r != null) {
3144                        result = Range.combine(result, r.findRangeBounds(d));
3145                    }
3146                    else {
3147                        result = Range.combine(
3148                            result, DatasetUtilities.findRangeBounds(d)
3149                        );
3150                    }
3151                }
3152            }
3153        }
3154        return result;
3155
3156    }
3157
3158    /**
3159     * Receives notification of a change to the plot's dataset.
3160     * <P>
3161     * The axis ranges are updated if necessary.
3162     *
3163     * @param event information about the event (not used here).
3164     */

3165    public void datasetChanged(DatasetChangeEvent event) {
3166        configureDomainAxes();
3167        configureRangeAxes();
3168        if (getParent() != null) {
3169            getParent().datasetChanged(event);
3170        }
3171        else {
3172            PlotChangeEvent e = new PlotChangeEvent(this);
3173            e.setType(ChartChangeEventType.DATASET_UPDATED);
3174            notifyListeners(e);
3175        }
3176    }
3177
3178    /**
3179     * Receives notification of a renderer change event.
3180     *
3181     * @param event the event.
3182     */

3183    public void rendererChanged(RendererChangeEvent event) {
3184        notifyListeners(new PlotChangeEvent(this));
3185    }
3186
3187    /**
3188     * Returns a flag indicating whether or not the domain crosshair is visible.
3189     *
3190     * @return The flag.
3191     */

3192    public boolean isDomainCrosshairVisible() {
3193        return this.domainCrosshairVisible;
3194    }
3195
3196    /**
3197     * Sets the flag indicating whether or not the domain crosshair is visible.
3198     *
3199     * @param flag the new value of the flag.
3200     */

3201    public void setDomainCrosshairVisible(boolean flag) {
3202
3203        if (this.domainCrosshairVisible != flag) {
3204            this.domainCrosshairVisible = flag;
3205            notifyListeners(new PlotChangeEvent(this));
3206        }
3207
3208    }
3209
3210    /**
3211     * Returns a flag indicating whether or not the crosshair should "lock-on"
3212     * to actual data values.
3213     *
3214     * @return The flag.
3215     */

3216    public boolean isDomainCrosshairLockedOnData() {
3217        return this.domainCrosshairLockedOnData;
3218    }
3219
3220    /**
3221     * Sets the flag indicating whether or not the domain crosshair should
3222     * "lock-on" to actual data values.
3223     *
3224     * @param flag the flag.
3225     */

3226    public void setDomainCrosshairLockedOnData(boolean flag) {
3227
3228        if (this.domainCrosshairLockedOnData != flag) {
3229            this.domainCrosshairLockedOnData = flag;
3230            notifyListeners(new PlotChangeEvent(this));
3231        }
3232
3233    }
3234
3235    /**
3236     * Returns the domain crosshair value.
3237     *
3238     * @return The value.
3239     */

3240    public double getDomainCrosshairValue() {
3241        return this.domainCrosshairValue;
3242    }
3243
3244    /**
3245     * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to
3246     * all registered listeners (provided that the domain crosshair is visible).
3247     *
3248     * @param value the value.
3249     */

3250    public void setDomainCrosshairValue(double value) {
3251        setDomainCrosshairValue(value, true);
3252    }
3253
3254    /**
3255     * Sets the domain crosshair value and, if requested, sends a
3256     * {@link PlotChangeEvent} to all registered listeners (provided that the
3257     * domain crosshair is visible).
3258     *
3259     * @param value the new value.
3260     * @param notify notify listeners?
3261     */

3262    public void setDomainCrosshairValue(double value, boolean notify) {
3263        this.domainCrosshairValue = value;
3264        if (isDomainCrosshairVisible() && notify) {
3265            notifyListeners(new PlotChangeEvent(this));
3266        }
3267    }
3268
3269    /**
3270     * Returns the {@link Stroke} used to draw the crosshair (if visible).
3271     *
3272     * @return The crosshair stroke.
3273     */

3274    public Stroke JavaDoc getDomainCrosshairStroke() {
3275        return this.domainCrosshairStroke;
3276    }
3277
3278    /**
3279     * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3280     * registered listeners that the axis has been modified.
3281     *
3282     * @param stroke the new crosshair stroke.
3283     */

3284    public void setDomainCrosshairStroke(Stroke JavaDoc stroke) {
3285        this.domainCrosshairStroke = stroke;
3286        notifyListeners(new PlotChangeEvent(this));
3287    }
3288
3289    /**
3290     * Returns the domain crosshair color.
3291     *
3292     * @return The crosshair color.
3293     */

3294    public Paint JavaDoc getDomainCrosshairPaint() {
3295        return this.domainCrosshairPaint;
3296    }
3297
3298    /**
3299     * Sets the Paint used to color the crosshairs (if visible) and notifies
3300     * registered listeners that the axis has been modified.
3301     *
3302     * @param paint the new crosshair paint.
3303     */

3304    public void setDomainCrosshairPaint(Paint JavaDoc paint) {
3305        this.domainCrosshairPaint = paint;
3306        notifyListeners(new PlotChangeEvent(this));
3307    }
3308
3309    /**
3310     * Returns a flag indicating whether or not the range crosshair is visible.
3311     *
3312     * @return The flag.
3313     */

3314    public boolean isRangeCrosshairVisible() {
3315        return this.rangeCrosshairVisible;
3316    }
3317
3318    /**
3319     * Sets the flag indicating whether or not the range crosshair is visible.
3320     *
3321     * @param flag the new value of the flag.
3322     */

3323    public void setRangeCrosshairVisible(boolean flag) {
3324
3325        if (this.rangeCrosshairVisible != flag) {
3326            this.rangeCrosshairVisible = flag;
3327            notifyListeners(new PlotChangeEvent(this));
3328        }
3329
3330    }
3331
3332    /**
3333     * Returns a flag indicating whether or not the crosshair should "lock-on"
3334     * to actual data values.
3335     *
3336     * @return The flag.
3337     */

3338    public boolean isRangeCrosshairLockedOnData() {
3339        return this.rangeCrosshairLockedOnData;
3340    }
3341
3342    /**
3343     * Sets the flag indicating whether or not the range crosshair should
3344     * "lock-on" to actual data values.
3345     *
3346     * @param flag the flag.
3347     */

3348    public void setRangeCrosshairLockedOnData(boolean flag) {
3349
3350        if (this.rangeCrosshairLockedOnData != flag) {
3351            this.rangeCrosshairLockedOnData = flag;
3352            notifyListeners(new PlotChangeEvent(this));
3353        }
3354
3355    }
3356
3357    /**
3358     * Returns the range crosshair value.
3359     *
3360     * @return The value.
3361     */

3362    public double getRangeCrosshairValue() {
3363        return this.rangeCrosshairValue;
3364    }
3365
3366    /**
3367     * Sets the domain crosshair value.
3368     * <P>
3369     * Registered listeners are notified that the plot has been modified, but
3370     * only if the crosshair is visible.
3371     *
3372     * @param value the new value.
3373     */

3374    public void setRangeCrosshairValue(double value) {
3375        setRangeCrosshairValue(value, true);
3376    }
3377
3378    /**
3379     * Sets the range crosshair value.
3380     * <P>
3381     * Registered listeners are notified that the axis has been modified, but
3382     * only if the crosshair is visible.
3383     *
3384     * @param value the new value.
3385     * @param notify a flag that controls whether or not listeners are
3386     * notified.
3387     */

3388    public void setRangeCrosshairValue(double value, boolean notify) {
3389        this.rangeCrosshairValue = value;
3390        if (isRangeCrosshairVisible() && notify) {
3391            notifyListeners(new PlotChangeEvent(this));
3392        }
3393    }
3394
3395    /**
3396     * Returns the Stroke used to draw the crosshair (if visible).
3397     *
3398     * @return The crosshair stroke.
3399     */

3400    public Stroke JavaDoc getRangeCrosshairStroke() {
3401        return this.rangeCrosshairStroke;
3402    }
3403
3404    /**
3405     * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3406     * registered listeners that the axis has been modified.
3407     *
3408     * @param stroke the new crosshair stroke.
3409     */

3410    public void setRangeCrosshairStroke(Stroke JavaDoc stroke) {
3411        this.rangeCrosshairStroke = stroke;
3412        notifyListeners(new PlotChangeEvent(this));
3413    }
3414
3415    /**
3416     * Returns the range crosshair color.
3417     *
3418     * @return The crosshair color.
3419     */

3420    public Paint JavaDoc getRangeCrosshairPaint() {
3421        return this.rangeCrosshairPaint;
3422    }
3423
3424    /**
3425     * Sets the Paint used to color the crosshairs (if visible) and notifies
3426     * registered listeners that the axis has been modified.
3427     *
3428     * @param paint the new crosshair paint.
3429     */

3430    public void setRangeCrosshairPaint(Paint JavaDoc paint) {
3431        this.rangeCrosshairPaint = paint;
3432        notifyListeners(new PlotChangeEvent(this));
3433    }
3434
3435    /**
3436     * Returns the fixed domain axis space.
3437     *
3438     * @return The fixed domain axis space (possibly <code>null</code>).
3439     */

3440    public AxisSpace getFixedDomainAxisSpace() {
3441        return this.fixedDomainAxisSpace;
3442    }
3443
3444    /**
3445     * Sets the fixed domain axis space.
3446     *
3447     * @param space the space.
3448     */

3449    public void setFixedDomainAxisSpace(AxisSpace space) {
3450        this.fixedDomainAxisSpace = space;
3451    }
3452
3453    /**
3454     * Returns the fixed range axis space.
3455     *
3456     * @return The fixed range axis space.
3457     */

3458    public AxisSpace getFixedRangeAxisSpace() {
3459        return this.fixedRangeAxisSpace;
3460    }
3461
3462    /**
3463     * Sets the fixed range axis space.
3464     *
3465     * @param space the space.
3466     */

3467    public void setFixedRangeAxisSpace(AxisSpace space) {
3468        this.fixedRangeAxisSpace = space;
3469    }
3470
3471    /**
3472     * Multiplies the range on the domain axis/axes by the specified factor.
3473     *
3474     * @param factor the zoom factor.
3475     * @param info the plot rendering info.
3476     * @param source the source point.
3477     */

3478    public void zoomDomainAxes(double factor, PlotRenderingInfo info,
3479                               Point2D JavaDoc source) {
3480        for (int i = 0; i < this.domainAxes.size(); i++) {
3481            ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
3482            if (domainAxis != null) {
3483                domainAxis.resizeRange(factor);
3484            }
3485        }
3486    }
3487
3488    /**
3489     * Zooms in on the domain axis/axes. The new lower and upper bounds are
3490     * specified as percentages of the current axis range, where 0 percent is
3491     * the current lower bound and 100 percent is the current upper bound.
3492     *
3493     * @param lowerPercent a percentage that determines the new lower bound
3494     * for the axis (e.g. 0.20 is twenty percent).
3495     * @param upperPercent a percentage that determines the new upper bound
3496     * for the axis (e.g. 0.80 is eighty percent).
3497     * @param info the plot rendering info.
3498     * @param source the source point.
3499     */

3500    public void zoomDomainAxes(double lowerPercent, double upperPercent,
3501                               PlotRenderingInfo info, Point2D JavaDoc source) {
3502        for (int i = 0; i < this.domainAxes.size(); i++) {
3503            ValueAxis domainAxis = (ValueAxis) this.domainAxes.get(i);
3504            if (domainAxis != null) {
3505                domainAxis.zoomRange(lowerPercent, upperPercent);
3506            }
3507        }
3508    }
3509
3510    /**
3511     * Multiplies the range on the range axis/axes by the specified factor.
3512     *
3513     * @param factor the zoom factor.
3514     * @param info the plot rendering info.
3515     * @param source the source point.
3516     */

3517    public void zoomRangeAxes(double factor, PlotRenderingInfo info,
3518                              Point2D JavaDoc source) {
3519        for (int i = 0; i < this.rangeAxes.size(); i++) {
3520            ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
3521            if (rangeAxis != null) {
3522                rangeAxis.resizeRange(factor);
3523            }
3524        }
3525    }
3526
3527    /**
3528     * Zooms in on the range axes.
3529     *
3530     * @param lowerPercent the lower bound.
3531     * @param upperPercent the upper bound.
3532     * @param info the plot rendering info.
3533     * @param source the source point.
3534     */

3535    public void zoomRangeAxes(double lowerPercent, double upperPercent,
3536                              PlotRenderingInfo info, Point2D JavaDoc source) {
3537        for (int i = 0; i < this.rangeAxes.size(); i++) {
3538            ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
3539            if (rangeAxis != null) {
3540                rangeAxis.zoomRange(lowerPercent, upperPercent);
3541            }
3542        }
3543    }
3544
3545    /**
3546     * Returns <code>true</code>
3547     *
3548     * @return A boolean.
3549     */

3550    public boolean isDomainZoomable() {
3551        return true;
3552    }
3553
3554    /**
3555     * Returns <code>true</code>
3556     *
3557     * @return A boolean.
3558     */

3559    public boolean isRangeZoomable() {
3560        return true;
3561    }
3562
3563    /**
3564     * Returns the number of series in the primary dataset for this plot. If
3565     * the dataset is <code>null</code>, the method returns 0.
3566     *
3567     * @return The series count.
3568     */

3569    public int getSeriesCount() {
3570        int result = 0;
3571        XYDataset dataset = getDataset();
3572        if (dataset != null) {
3573            result = dataset.getSeriesCount();
3574        }
3575        return result;
3576    }
3577
3578    /**
3579     * Returns the fixed legend items, if any.
3580     *
3581     * @return The legend items (possibly <code>null</code>).
3582     */

3583    public LegendItemCollection getFixedLegendItems() {
3584        return this.fixedLegendItems;
3585    }
3586
3587    /**
3588     * Sets the fixed legend items for the plot. Leave this set to
3589     * <code>null</code> if you prefer the legend items to be created
3590     * automatically.
3591     *
3592     * @param items the legend items (<code>null</code> permitted).
3593     */

3594    public void setFixedLegendItems(LegendItemCollection items) {
3595        this.fixedLegendItems = items;
3596        notifyListeners(new PlotChangeEvent(this));
3597    }
3598
3599    /**
3600     * Returns the legend items for the plot. Each legend item is generated by
3601     * the plot's renderer, since the renderer is responsible for the visual
3602     * representation of the data.
3603     *
3604     * @return The legend items.
3605     */

3606    public LegendItemCollection getLegendItems() {
3607        if (this.fixedLegendItems != null) {
3608            return this.fixedLegendItems;
3609        }
3610        LegendItemCollection result = new LegendItemCollection();
3611        int count = this.datasets.size();
3612        for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
3613            XYDataset dataset = getDataset(datasetIndex);
3614            if (dataset != null) {
3615                XYItemRenderer renderer = getRenderer(datasetIndex);
3616                if (renderer == null) {
3617                    renderer = getRenderer(0);
3618                }
3619                if (renderer != null) {
3620                    int seriesCount = dataset.getSeriesCount();
3621                    for (int i = 0; i < seriesCount; i++) {
3622                        if (renderer.isSeriesVisible(i)
3623                                && renderer.isSeriesVisibleInLegend(i)) {
3624                            LegendItem item = renderer.getLegendItem(
3625                                datasetIndex, i
3626                            );
3627                            if (item != null) {
3628                                result.add(item);
3629                            }
3630                        }
3631                    }
3632                }
3633            }
3634        }
3635        return result;
3636    }
3637
3638    /**
3639     * Tests this plot for equality with another object.
3640     *
3641     * @param obj the object (<code>null</code> permitted).
3642     *
3643     * @return <code>true</code> or <code>false</code>.
3644     */

3645    public boolean equals(Object JavaDoc obj) {
3646
3647        if (obj == this) {
3648            return true;
3649        }
3650        if (!(obj instanceof XYPlot)) {
3651            return false;
3652        }
3653        if (!super.equals(obj)) {
3654            return false;
3655        }
3656
3657        XYPlot that = (XYPlot) obj;
3658        if (this.weight != that.weight) {
3659            return false;
3660        }
3661        if (this.orientation != that.orientation) {
3662            return false;
3663        }
3664        if (!this.domainAxes.equals(that.domainAxes)) {
3665            return false;
3666        }
3667        if (!this.domainAxisLocations.equals(that.domainAxisLocations)) {
3668            return false;
3669        }
3670        if (this.rangeCrosshairLockedOnData
3671                != that.rangeCrosshairLockedOnData) {
3672            return false;
3673        }
3674        if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
3675            return false;
3676        }
3677        if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) {
3678            return false;
3679        }
3680        if (this.rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) {
3681            return false;
3682        }
3683        if (this.domainCrosshairVisible != that.domainCrosshairVisible) {
3684            return false;
3685        }
3686        if (this.domainCrosshairValue != that.domainCrosshairValue) {
3687            return false;
3688        }
3689        if (this.domainCrosshairLockedOnData
3690                != that.domainCrosshairLockedOnData) {
3691            return false;
3692        }
3693        if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) {
3694            return false;
3695        }
3696        if (this.rangeCrosshairValue != that.rangeCrosshairValue) {
3697            return false;
3698        }
3699        if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) {
3700            return false;
3701        }
3702        if (!ObjectUtilities.equal(this.renderers, that.renderers)) {
3703            return false;
3704        }
3705        if (!ObjectUtilities.equal(this.rangeAxes, that.rangeAxes)) {
3706            return false;
3707        }
3708        if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) {
3709            return false;
3710        }
3711        if (!ObjectUtilities.equal(
3712                this.datasetToDomainAxisMap, that.datasetToDomainAxisMap
3713            )) {
3714            return false;
3715        }
3716        if (!ObjectUtilities.equal(
3717                this.datasetToRangeAxisMap, that.datasetToRangeAxisMap
3718            )) {
3719            return false;
3720        }
3721        if (!ObjectUtilities.equal(
3722                this.domainGridlineStroke, that.domainGridlineStroke)) {
3723            return false;
3724        }
3725        if (!ObjectUtilities.equal(
3726                this.domainGridlinePaint, that.domainGridlinePaint)) {
3727            return false;
3728        }
3729        if (!ObjectUtilities.equal(
3730                this.rangeGridlineStroke, that.rangeGridlineStroke)) {
3731            return false;
3732        }
3733        if (!ObjectUtilities.equal(
3734                this.rangeGridlinePaint, that.rangeGridlinePaint)) {
3735            return false;
3736        }
3737        if (!ObjectUtilities.equal(
3738                this.rangeZeroBaselinePaint, that.rangeZeroBaselinePaint
3739            )) {
3740            return false;
3741        }
3742        if (!ObjectUtilities.equal(
3743                this.rangeZeroBaselineStroke, that.rangeZeroBaselineStroke
3744            )) {
3745            return false;
3746        }
3747        if (!ObjectUtilities.equal(
3748                this.domainCrosshairStroke, that.domainCrosshairStroke
3749            )) {
3750            return false;
3751        }
3752        if (!ObjectUtilities.equal(
3753                this.domainCrosshairPaint, that.domainCrosshairPaint
3754            )) {
3755            return false;
3756        }
3757        if (!ObjectUtilities.equal(
3758                this.rangeCrosshairStroke, that.rangeCrosshairStroke
3759            )) {
3760            return false;
3761        }
3762        if (!ObjectUtilities.equal(
3763                this.rangeCrosshairPaint, that.rangeCrosshairPaint
3764            )) {
3765            return false;
3766        }
3767        if (!ObjectUtilities.equal(
3768                this.foregroundDomainMarkers, that.foregroundDomainMarkers
3769            )) {
3770            return false;
3771        }
3772        if (!ObjectUtilities.equal(
3773                this.backgroundDomainMarkers, that.backgroundDomainMarkers
3774            )) {
3775            return false;
3776        }
3777        if (!ObjectUtilities.equal(
3778                this.foregroundRangeMarkers, that.foregroundRangeMarkers
3779            )) {
3780            return false;
3781        }
3782        if (!ObjectUtilities.equal(
3783                this.backgroundRangeMarkers, that.backgroundRangeMarkers
3784            )) {
3785            return false;
3786        }
3787        if (!ObjectUtilities.equal(
3788                this.foregroundDomainMarkers, that.foregroundDomainMarkers
3789            )) {
3790            return false;
3791        }
3792        if (!ObjectUtilities.equal(
3793                this.backgroundDomainMarkers, that.backgroundDomainMarkers
3794            )) {
3795            return false;
3796        }
3797        if (!ObjectUtilities.equal(
3798                this.foregroundRangeMarkers, that.foregroundRangeMarkers
3799            )) {
3800            return false;
3801        }
3802        if (!ObjectUtilities.equal(
3803                this.backgroundRangeMarkers, that.backgroundRangeMarkers
3804            )) {
3805            return false;
3806        }
3807        if (!ObjectUtilities.equal(
3808                this.annotations, that.annotations
3809            )) {
3810            return false;
3811        }
3812        return true;
3813
3814    }
3815
3816    /**
3817     * Returns a clone of the plot.
3818     *
3819     * @return A clone.
3820     *
3821     * @throws CloneNotSupportedException this can occur if some component of
3822     * the plot cannot be cloned.
3823     */

3824    public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
3825
3826        XYPlot clone = (XYPlot) super.clone();
3827        clone.domainAxes = (ObjectList) ObjectUtilities.clone(this.domainAxes);
3828        for (int i = 0; i < this.domainAxes.size(); i++) {
3829            ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
3830            if (axis != null) {
3831                ValueAxis clonedAxis = (ValueAxis) axis.clone();
3832                clone.domainAxes.set(i, clonedAxis);
3833                clonedAxis.setPlot(clone);
3834                clonedAxis.addChangeListener(clone);
3835            }
3836        }
3837        clone.domainAxisLocations
3838            = (ObjectList) this.domainAxisLocations.clone();
3839
3840        clone.rangeAxes = (ObjectList) ObjectUtilities.clone(this.rangeAxes);
3841        for (int i = 0; i < this.rangeAxes.size(); i++) {
3842            ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
3843            if (axis != null) {
3844                ValueAxis clonedAxis = (ValueAxis) axis.clone();
3845                clone.rangeAxes.set(i, clonedAxis);
3846                clonedAxis.setPlot(clone);
3847                clonedAxis.addChangeListener(clone);
3848            }
3849        }
3850        clone.rangeAxisLocations
3851            = (ObjectList) ObjectUtilities.clone(this.rangeAxisLocations);
3852
3853        // the datasets are not cloned, but listeners need to be added...
3854
clone.datasets = (ObjectList) ObjectUtilities.clone(this.datasets);
3855        for (int i = 0; i < clone.datasets.size(); ++i) {
3856            XYDataset d = getDataset(i);
3857            if (d != null) {
3858                d.addChangeListener(clone);
3859            }
3860        }
3861
3862        clone.datasetToDomainAxisMap = new TreeMap JavaDoc();
3863        clone.datasetToDomainAxisMap.putAll(this.datasetToDomainAxisMap);
3864        clone.datasetToRangeAxisMap = new TreeMap JavaDoc();
3865        clone.datasetToRangeAxisMap.putAll(this.datasetToRangeAxisMap);
3866
3867        clone.renderers = (ObjectList) ObjectUtilities.clone(this.renderers);
3868        for (int i = 0; i < this.renderers.size(); i++) {
3869            XYItemRenderer renderer2 = (XYItemRenderer) this.renderers.get(i);
3870            if (renderer2 instanceof PublicCloneable) {
3871                PublicCloneable pc = (PublicCloneable) renderer2;
3872                clone.renderers.set(i, pc.clone());
3873            }
3874        }
3875        clone.foregroundDomainMarkers = (Map JavaDoc) ObjectUtilities.clone(
3876            this.foregroundDomainMarkers
3877        );
3878        clone.backgroundDomainMarkers = (Map JavaDoc) ObjectUtilities.clone(
3879            this.backgroundDomainMarkers
3880        );
3881        clone.foregroundRangeMarkers = (Map JavaDoc) ObjectUtilities.clone(
3882            this.foregroundRangeMarkers
3883        );
3884        clone.backgroundRangeMarkers = (Map JavaDoc) ObjectUtilities.clone(
3885            this.backgroundRangeMarkers
3886        );
3887        clone.annotations = (List JavaDoc) ObjectUtilities.deepClone(this.annotations);
3888        if (this.fixedDomainAxisSpace != null) {
3889            clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone(
3890                this.fixedDomainAxisSpace
3891            );
3892        }
3893        if (this.fixedRangeAxisSpace != null) {
3894            clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone(
3895                this.fixedRangeAxisSpace
3896            );
3897        }
3898        return clone;
3899
3900    }
3901
3902    /**
3903     * Provides serialization support.
3904     *
3905     * @param stream the output stream.
3906     *
3907     * @throws IOException if there is an I/O error.
3908     */

3909    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
3910        stream.defaultWriteObject();
3911        SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
3912        SerialUtilities.writePaint(this.domainGridlinePaint, stream);
3913        SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
3914        SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
3915        SerialUtilities.writeStroke(this.rangeZeroBaselineStroke, stream);
3916        SerialUtilities.writePaint(this.rangeZeroBaselinePaint, stream);
3917        SerialUtilities.writeStroke(this.domainCrosshairStroke, stream);
3918        SerialUtilities.writePaint(this.domainCrosshairPaint, stream);
3919        SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream);
3920        SerialUtilities.writePaint(this.rangeCrosshairPaint, stream);
3921        SerialUtilities.writePaint(this.domainTickBandPaint, stream);
3922        SerialUtilities.writePaint(this.rangeTickBandPaint, stream);
3923        SerialUtilities.writePoint2D(this.quadrantOrigin, stream);
3924        for (int i = 0; i < 4; i++) {
3925            SerialUtilities.writePaint(this.quadrantPaint[i], stream);
3926        }
3927    }
3928
3929    /**
3930     * Provides serialization support.
3931     *
3932     * @param stream the input stream.
3933     *
3934     * @throws IOException if there is an I/O error.
3935     * @throws ClassNotFoundException if there is a classpath problem.
3936     */

3937    private void readObject(ObjectInputStream JavaDoc stream)
3938        throws IOException JavaDoc, ClassNotFoundException JavaDoc {
3939
3940        stream.defaultReadObject();
3941        this.domainGridlineStroke = SerialUtilities.readStroke(stream);
3942        this.domainGridlinePaint = SerialUtilities.readPaint(stream);
3943        this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
3944        this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
3945        this.rangeZeroBaselineStroke = SerialUtilities.readStroke(stream);
3946        this.rangeZeroBaselinePaint = SerialUtilities.readPaint(stream);
3947        this.domainCrosshairStroke = SerialUtilities.readStroke(stream);
3948        this.domainCrosshairPaint = SerialUtilities.readPaint(stream);
3949        this.rangeCrosshairStroke = SerialUtilities.readStroke(stream);
3950        this.rangeCrosshairPaint = SerialUtilities.readPaint(stream);
3951        this.domainTickBandPaint = SerialUtilities.readPaint(stream);
3952        this.rangeTickBandPaint = SerialUtilities.readPaint(stream);
3953        this.quadrantOrigin = SerialUtilities.readPoint2D(stream);
3954        this.quadrantPaint = new Paint JavaDoc[4];
3955        for (int i = 0; i < 4; i++) {
3956            this.quadrantPaint[i] = SerialUtilities.readPaint(stream);
3957        }
3958
3959        // register the plot as a listener with its axes, datasets, and
3960
// renderers...
3961
int domainAxisCount = this.domainAxes.size();
3962        for (int i = 0; i < domainAxisCount; i++) {
3963            Axis axis = (Axis) this.domainAxes.get(i);
3964            if (axis != null) {
3965                axis.setPlot(this);
3966                axis.addChangeListener(this);
3967            }
3968        }
3969        int rangeAxisCount = this.rangeAxes.size();
3970        for (int i = 0; i < rangeAxisCount; i++) {
3971            Axis axis = (Axis) this.rangeAxes.get(i);
3972            if (axis != null) {
3973                axis.setPlot(this);
3974                axis.addChangeListener(this);
3975            }
3976        }
3977        int datasetCount = this.datasets.size();
3978        for (int i = 0; i < datasetCount; i++) {
3979            Dataset dataset = (Dataset) this.datasets.get(i);
3980            if (dataset != null) {
3981                dataset.addChangeListener(this);
3982            }
3983        }
3984        int rendererCount = this.renderers.size();
3985        for (int i = 0; i < rendererCount; i++) {
3986            XYItemRenderer renderer = (XYItemRenderer) this.renderers.get(i);
3987            if (renderer != null) {
3988                renderer.addChangeListener(this);
3989            }
3990        }
3991    
3992    }
3993
3994}
3995
Popular Tags