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