KickJava   Java API By Example, From Geeks To Geeks.

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


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  * CategoryPlot.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): Jeremy Bowman;
33  * Arnaud Lelievre;
34  *
35  * $Id: CategoryPlot.java,v 1.23 2005/06/02 08:47:15 mungady Exp $
36  *
37  * Changes (from 21-Jun-2001)
38  * --------------------------
39  * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
40  * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
41  * 18-Sep-2001 : Updated header (DG);
42  * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
43  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
44  * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
45  * available space rather than a fixed number of units (DG);
46  * 12-Dec-2001 : Changed constructors to protected (DG);
47  * 13-Dec-2001 : Added tooltips (DG);
48  * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
49  * some argument checking code. Thanks to Taoufik Romdhane for
50  * suggesting this (DG);
51  * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
52  * alpha-transparency for Plot and subclasses (DG);
53  * 06-Mar-2002 : Updated import statements (DG);
54  * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
55  * to use the CategoryItemRenderer interface (DG);
56  * 22-Mar-2002 : Dropped the getCategories() method (DG);
57  * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
58  * class (DG);
59  * 29-Apr-2002 : New methods to support printing values at the end of bars,
60  * contributed by Jeremy Bowman (DG);
61  * 11-May-2002 : New methods for label visibility and overlaid plot support,
62  * contributed by Jeremy Bowman (DG);
63  * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
64  * renderer. Moved constants into the CategoryPlotConstants
65  * interface. Updated Javadoc comments (DG);
66  * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
67  * lower bound on the range axis (if necessary), updated
68  * Javadocs (DG);
69  * 25-Jun-2002 : Removed redundant imports (DG);
70  * 20-Aug-2002 : Changed the constructor for Marker (DG);
71  * 28-Aug-2002 : Added listener notification to setDomainAxis() and
72  * setRangeAxis() (DG);
73  * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
74  * Checkstyle (DG);
75  * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
76  * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
77  * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
78  * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
79  * these were set in the axes) (DG);
80  * 19-Nov-2002 : Added axis location parameters to constructor (DG);
81  * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
82  * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
83  * 26-Mar-2003 : Implemented Serializable (DG);
84  * 02-May-2003 : Moved render() method up from subclasses. Added secondary
85  * range markers. Added an attribute to control the dataset
86  * rendering order. Added a drawAnnotations() method. Changed
87  * the axis location from an int to an AxisLocation (DG);
88  * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
89  * this class (DG);
90  * 02-Jun-2003 : Removed check for range axis compatibility (DG);
91  * 04-Jul-2003 : Added a domain gridline position attribute (DG);
92  * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
93  * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
94  * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
95  * changes) (DG);
96  * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
97  * 790407 (initialise method) (DG);
98  * 08-Sep-2003 : Added internationalization via use of properties
99  * resourceBundle (RFE 690236) (AL);
100  * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed
101  * ValueAxis API (DG);
102  * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
103  * 15-Sep-2003 : Fixed two bugs in serialization, implemented
104  * PublicCloneable (DG);
105  * 23-Oct-2003 : Added event notification for changes to renderer (DG);
106  * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
107  * 03-Dec-2003 : Modified draw method to accept anchor (DG);
108  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
109  * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
110  * stacked (DG);
111  * 12-May-2004 : Added fixed legend items (DG);
112  * 19-May-2004 : Added check for null legend item from renderer (DG);
113  * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
114  * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
115  * --> datasetsMappedToRangeAxis(), and ensured that returned
116  * list doesn't contain null datasets (DG);
117  * 12-Nov-2004 : Implemented new Zoomable interface (DG);
118  * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
119  * CategoryItemRenderer (DG);
120  * 04-May-2005 : Fixed serialization of range markers (DG);
121  * 05-May-2005 : Updated draw() method parameters (DG);
122  * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
123  * RFE 1183100 (DG);
124  * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
125  * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
126  * 02-Jun-2005 : Added support for domain markers (DG);
127  *
128  */

129
130 package org.jfree.chart.plot;
131
132 import java.awt.AlphaComposite JavaDoc;
133 import java.awt.BasicStroke JavaDoc;
134 import java.awt.Color JavaDoc;
135 import java.awt.Composite JavaDoc;
136 import java.awt.Font JavaDoc;
137 import java.awt.Graphics2D JavaDoc;
138 import java.awt.Paint JavaDoc;
139 import java.awt.Shape JavaDoc;
140 import java.awt.Stroke JavaDoc;
141 import java.awt.geom.Line2D JavaDoc;
142 import java.awt.geom.Point2D JavaDoc;
143 import java.awt.geom.Rectangle2D JavaDoc;
144 import java.io.IOException JavaDoc;
145 import java.io.ObjectInputStream JavaDoc;
146 import java.io.ObjectOutputStream JavaDoc;
147 import java.io.Serializable JavaDoc;
148 import java.util.ArrayList JavaDoc;
149 import java.util.Collection JavaDoc;
150 import java.util.Collections JavaDoc;
151 import java.util.HashMap JavaDoc;
152 import java.util.Iterator JavaDoc;
153 import java.util.List JavaDoc;
154 import java.util.Map JavaDoc;
155 import java.util.ResourceBundle JavaDoc;
156
157 import org.jfree.chart.LegendItem;
158 import org.jfree.chart.LegendItemCollection;
159 import org.jfree.chart.annotations.CategoryAnnotation;
160 import org.jfree.chart.axis.Axis;
161 import org.jfree.chart.axis.AxisCollection;
162 import org.jfree.chart.axis.AxisLocation;
163 import org.jfree.chart.axis.AxisSpace;
164 import org.jfree.chart.axis.AxisState;
165 import org.jfree.chart.axis.CategoryAnchor;
166 import org.jfree.chart.axis.CategoryAxis;
167 import org.jfree.chart.axis.ValueAxis;
168 import org.jfree.chart.axis.ValueTick;
169 import org.jfree.chart.event.ChartChangeEventType;
170 import org.jfree.chart.event.PlotChangeEvent;
171 import org.jfree.chart.event.RendererChangeEvent;
172 import org.jfree.chart.event.RendererChangeListener;
173 import org.jfree.chart.renderer.category.CategoryItemRenderer;
174 import org.jfree.chart.renderer.category.CategoryItemRendererState;
175 import org.jfree.data.Range;
176 import org.jfree.data.category.CategoryDataset;
177 import org.jfree.data.general.Dataset;
178 import org.jfree.data.general.DatasetChangeEvent;
179 import org.jfree.data.general.DatasetUtilities;
180 import org.jfree.io.SerialUtilities;
181 import org.jfree.ui.Layer;
182 import org.jfree.ui.RectangleEdge;
183 import org.jfree.ui.RectangleInsets;
184 import org.jfree.util.ObjectList;
185 import org.jfree.util.ObjectUtilities;
186 import org.jfree.util.PublicCloneable;
187 import org.jfree.util.SortOrder;
188
189 /**
190  * A general plotting class that uses data from a {@link CategoryDataset} and
191  * renders each data item using a {@link CategoryItemRenderer}.
192  */

193 public class CategoryPlot extends Plot
194                           implements ValueAxisPlot,
195                                      Zoomable,
196                                      RendererChangeListener,
197                                      Cloneable JavaDoc, PublicCloneable, Serializable JavaDoc {
198
199     /** For serialization. */
200     private static final long serialVersionUID = -3537691700434728188L;
201     
202     /**
203      * The default visibility of the grid lines plotted against the domain
204      * axis.
205      */

206     public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
207
208     /**
209      * The default visibility of the grid lines plotted against the range
210      * axis.
211      */

212     public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
213
214     /** The default grid line stroke. */
215     public static final Stroke JavaDoc DEFAULT_GRIDLINE_STROKE = new BasicStroke JavaDoc(0.5f,
216         BasicStroke.CAP_BUTT,
217         BasicStroke.JOIN_BEVEL,
218         0.0f,
219         new float[] {2.0f, 2.0f},
220         0.0f);
221
222     /** The default grid line paint. */
223     public static final Paint JavaDoc DEFAULT_GRIDLINE_PAINT = Color.lightGray;
224
225     /** The default value label font. */
226     public static final Font JavaDoc DEFAULT_VALUE_LABEL_FONT
227         = new Font JavaDoc("SansSerif", Font.PLAIN, 10);
228
229     /** The resourceBundle for the localization. */
230     protected static ResourceBundle JavaDoc localizationResources
231         = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
232
233     /** The plot orientation. */
234     private PlotOrientation orientation;
235
236     /** The offset between the data area and the axes. */
237     private RectangleInsets axisOffset;
238
239     /** Storage for the domain axes. */
240     private ObjectList domainAxes;
241
242     /** Storage for the domain axis locations. */
243     private ObjectList domainAxisLocations;
244
245     /**
246      * A flag that controls whether or not the shared domain axis is drawn
247      * (only relevant when the plot is being used as a subplot).
248      */

249     private boolean drawSharedDomainAxis;
250
251     /** Storage for the range axes. */
252     private ObjectList rangeAxes;
253
254     /** Storage for the range axis locations. */
255     private ObjectList rangeAxisLocations;
256
257     /** Storage for the datasets. */
258     private ObjectList datasets;
259
260     /** Storage for keys that map datasets to domain axes. */
261     private ObjectList datasetToDomainAxisMap;
262     
263     /** Storage for keys that map datasets to range axes. */
264     private ObjectList datasetToRangeAxisMap;
265
266     /** Storage for the renderers. */
267     private ObjectList renderers;
268
269     /** The dataset rendering order. */
270     private DatasetRenderingOrder renderingOrder
271         = DatasetRenderingOrder.REVERSE;
272
273     /**
274      * Controls the order in which the columns are traversed when rendering the
275      * data items.
276      */

277     private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
278     
279     /**
280      * Controls the order in which the rows are traversed when rendering the
281      * data items.
282      */

283     private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
284     
285     /**
286      * A flag that controls whether the grid-lines for the domain axis are
287      * visible.
288      */

289     private boolean domainGridlinesVisible;
290
291     /** The position of the domain gridlines relative to the category. */
292     private CategoryAnchor domainGridlinePosition;
293
294     /** The stroke used to draw the domain grid-lines. */
295     private transient Stroke JavaDoc domainGridlineStroke;
296
297     /** The paint used to draw the domain grid-lines. */
298     private transient Paint JavaDoc domainGridlinePaint;
299
300     /**
301      * A flag that controls whether the grid-lines for the range axis are
302      * visible.
303      */

304     private boolean rangeGridlinesVisible;
305
306     /** The stroke used to draw the range axis grid-lines. */
307     private transient Stroke JavaDoc rangeGridlineStroke;
308
309     /** The paint used to draw the range axis grid-lines. */
310     private transient Paint JavaDoc rangeGridlinePaint;
311
312     /** The anchor value. */
313     private double anchorValue;
314
315     /** A flag that controls whether or not a range crosshair is drawn..*/
316     private boolean rangeCrosshairVisible;
317
318     /** The range crosshair value. */
319     private double rangeCrosshairValue;
320
321     /** The pen/brush used to draw the crosshair (if any). */
322     private transient Stroke JavaDoc rangeCrosshairStroke;
323
324     /** The color used to draw the crosshair (if any). */
325     private transient Paint JavaDoc rangeCrosshairPaint;
326
327     /**
328      * A flag that controls whether or not the crosshair locks onto actual
329      * data points.
330      */

331     private boolean rangeCrosshairLockedOnData = true;
332
333     /** A map containing lists of markers for the domain axes. */
334     private Map JavaDoc foregroundDomainMarkers;
335
336     /** A map containing lists of markers for the domain axes. */
337     private Map JavaDoc backgroundDomainMarkers;
338
339     /** A map containing lists of markers for the range axes. */
340     private Map JavaDoc foregroundRangeMarkers;
341
342     /** A map containing lists of markers for the range axes. */
343     private Map JavaDoc backgroundRangeMarkers;
344
345     /**
346      * A (possibly empty) list of annotations for the plot. The list should
347      * be initialised in the constructor and never allowed to be
348      * <code>null</code>.
349      */

350     private List JavaDoc annotations;
351
352     /**
353      * The weight for the plot (only relevant when the plot is used as a subplot
354      * within a combined plot).
355      */

356     private int weight;
357
358     /** The fixed space for the domain axis. */
359     private AxisSpace fixedDomainAxisSpace;
360
361     /** The fixed space for the range axis. */
362     private AxisSpace fixedRangeAxisSpace;
363
364     /**
365      * An optional collection of legend items that can be returned by the
366      * getLegendItems() method.
367      */

368     private LegendItemCollection fixedLegendItems;
369     
370     /**
371      * Default constructor.
372      */

373     public CategoryPlot() {
374         this(null, null, null, null);
375     }
376
377     /**
378      * Creates a new plot.
379      *
380      * @param dataset the dataset (<code>null</code> permitted).
381      * @param domainAxis the domain axis (<code>null</code> permitted).
382      * @param rangeAxis the range axis (<code>null</code> permitted).
383      * @param renderer the item renderer (<code>null</code> permitted).
384      *
385      */

386     public CategoryPlot(CategoryDataset dataset,
387                         CategoryAxis domainAxis,
388                         ValueAxis rangeAxis,
389                         CategoryItemRenderer renderer) {
390
391         super();
392
393         this.orientation = PlotOrientation.VERTICAL;
394
395         // allocate storage for dataset, axes and renderers
396
this.domainAxes = new ObjectList();
397         this.domainAxisLocations = new ObjectList();
398         this.rangeAxes = new ObjectList();
399         this.rangeAxisLocations = new ObjectList();
400         
401         this.datasetToDomainAxisMap = new ObjectList();
402         this.datasetToRangeAxisMap = new ObjectList();
403
404         this.renderers = new ObjectList();
405
406         this.datasets = new ObjectList();
407         this.datasets.set(0, dataset);
408         if (dataset != null) {
409             dataset.addChangeListener(this);
410         }
411
412         this.axisOffset = RectangleInsets.ZERO_INSETS;
413
414         setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
415         setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
416
417         this.renderers.set(0, renderer);
418         if (renderer != null) {
419             renderer.setPlot(this);
420             renderer.addChangeListener(this);
421         }
422
423         this.domainAxes.set(0, domainAxis);
424         this.mapDatasetToDomainAxis(0, 0);
425         if (domainAxis != null) {
426             domainAxis.setPlot(this);
427             domainAxis.addChangeListener(this);
428         }
429         this.drawSharedDomainAxis = false;
430
431         this.rangeAxes.set(0, rangeAxis);
432         this.mapDatasetToRangeAxis(0, 0);
433         if (rangeAxis != null) {
434             rangeAxis.setPlot(this);
435             rangeAxis.addChangeListener(this);
436         }
437         
438         configureDomainAxes();
439         configureRangeAxes();
440
441         this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
442         this.domainGridlinePosition = CategoryAnchor.MIDDLE;
443         this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
444         this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
445
446         this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
447         this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
448         this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
449
450         this.foregroundDomainMarkers = new HashMap JavaDoc();
451         this.backgroundDomainMarkers = new HashMap JavaDoc();
452         this.foregroundRangeMarkers = new HashMap JavaDoc();
453         this.backgroundRangeMarkers = new HashMap JavaDoc();
454
455         Marker baseline = new ValueMarker(
456             0.0, new Color JavaDoc(0.8f, 0.8f, 0.8f, 0.5f), new BasicStroke JavaDoc(1.0f),
457             new Color JavaDoc(0.85f, 0.85f, 0.95f, 0.5f), new BasicStroke JavaDoc(1.0f), 0.6f
458         );
459         addRangeMarker(baseline, Layer.BACKGROUND);
460
461         this.anchorValue = 0.0;
462         this.annotations = new java.util.ArrayList JavaDoc();
463
464     }
465     
466     /**
467      * Returns a string describing the type of plot.
468      *
469      * @return The type.
470      */

471     public String JavaDoc getPlotType() {
472         return localizationResources.getString("Category_Plot");
473     }
474
475     /**
476      * Returns the orientation of the plot.
477      *
478      * @return The orientation of the plot.
479      */

480     public PlotOrientation getOrientation() {
481         return this.orientation;
482     }
483
484     /**
485      * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
486      * all registered listeners.
487      *
488      * @param orientation the orientation (<code>null</code> not permitted).
489      */

490     public void setOrientation(PlotOrientation orientation) {
491         if (orientation == null) {
492             throw new IllegalArgumentException JavaDoc("Null 'orientation' argument.");
493         }
494         this.orientation = orientation;
495         notifyListeners(new PlotChangeEvent(this));
496     }
497
498     /**
499      * Returns the axis offset.
500      *
501      * @return The axis offset (never <code>null</code>).
502      */

503     public RectangleInsets getAxisOffset() {
504         return this.axisOffset;
505     }
506
507     /**
508      * Sets the axis offsets (gap between the data area and the axes).
509      *
510      * @param offset the offset (<code>null</code> not permitted).
511      */

512     public void setAxisOffset(RectangleInsets offset) {
513         if (offset == null) {
514             throw new IllegalArgumentException JavaDoc("Null 'offset' argument.");
515         }
516         this.axisOffset = offset;
517         notifyListeners(new PlotChangeEvent(this));
518     }
519
520
521     /**
522      * Returns the domain axis for the plot. If the domain axis for this plot
523      * is <code>null</code>, then the method will return the parent plot's
524      * domain axis (if there is a parent plot).
525      *
526      * @return The domain axis (<code>null</code> permitted).
527      */

528     public CategoryAxis getDomainAxis() {
529         return getDomainAxis(0);
530     }
531
532     /**
533      * Returns a domain axis.
534      *
535      * @param index the axis index.
536      *
537      * @return The axis (<code>null</code> possible).
538      */

539     public CategoryAxis getDomainAxis(int index) {
540         CategoryAxis result = null;
541         if (index < this.domainAxes.size()) {
542             result = (CategoryAxis) this.domainAxes.get(index);
543         }
544         if (result == null) {
545             Plot parent = getParent();
546             if (parent instanceof CategoryPlot) {
547                 CategoryPlot cp = (CategoryPlot) parent;
548                 result = cp.getDomainAxis(index);
549             }
550         }
551         return result;
552     }
553
554     /**
555      * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
556      * all registered listeners.
557      *
558      * @param axis the axis (<code>null</code> permitted).
559      */

560     public void setDomainAxis(CategoryAxis axis) {
561         setDomainAxis(0, axis);
562     }
563
564     /**
565      * Sets a domain axis and sends a {@link PlotChangeEvent} to all
566      * registered listeners.
567      *
568      * @param index the axis index.
569      * @param axis the axis.
570      */

571     public void setDomainAxis(int index, CategoryAxis axis) {
572         setDomainAxis(index, axis, true);
573     }
574  
575     /**
576      * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
577      * all registered listeners.
578      *
579      * @param index the axis index.
580      * @param axis the axis.
581      * @param notify notify listeners?
582      */

583     public void setDomainAxis(int index, CategoryAxis axis, boolean notify) {
584         CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index);
585         if (existing != null) {
586             existing.removeChangeListener(this);
587         }
588         if (axis != null) {
589             axis.setPlot(this);
590         }
591         this.domainAxes.set(index, axis);
592         if (axis != null) {
593             axis.configure();
594             axis.addChangeListener(this);
595         }
596         if (notify) {
597             notifyListeners(new PlotChangeEvent(this));
598         }
599     }
600
601     /**
602      * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
603      * to all registered listeners.
604      *
605      * @param axes the axes.
606      */

607     public void setDomainAxes(CategoryAxis[] axes) {
608         for (int i = 0; i < axes.length; i++) {
609             setDomainAxis(i, axes[i], false);
610         }
611         notifyListeners(new PlotChangeEvent(this));
612     }
613     
614     /**
615      * Returns the domain axis location.
616      *
617      * @return The location (never <code>null</code>).
618      */

619     public AxisLocation getDomainAxisLocation() {
620         return getDomainAxisLocation(0);
621     }
622
623     /**
624      * Returns the location for a domain axis.
625      *
626      * @param index the axis index.
627      *
628      * @return The location.
629      */

630     public AxisLocation getDomainAxisLocation(int index) {
631         AxisLocation result = null;
632         if (index < this.domainAxisLocations.size()) {
633             result = (AxisLocation) this.domainAxisLocations.get(index);
634         }
635         if (result == null) {
636             result = AxisLocation.getOpposite(getDomainAxisLocation(0));
637         }
638         return result;
639
640     }
641
642     /**
643      * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
644      * to all registered listeners.
645      *
646      * @param location the axis location (<code>null</code> not permitted).
647      */

648     public void setDomainAxisLocation(AxisLocation location) {
649         // defer argument checking...
650
setDomainAxisLocation(location, true);
651     }
652
653     /**
654      * Sets the location of the domain axis.
655      *
656      * @param location the axis location (<code>null</code> not permitted).
657      * @param notify a flag that controls whether listeners are notified.
658      */

659     public void setDomainAxisLocation(AxisLocation location, boolean notify) {
660         if (location == null) {
661             throw new IllegalArgumentException JavaDoc("Null 'location' argument.");
662         }
663         setDomainAxisLocation(0, location);
664     }
665
666     /**
667      * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
668      * to all registered listeners.
669      *
670      * @param index the axis index.
671      * @param location the location.
672      */

673     public void setDomainAxisLocation(int index, AxisLocation location) {
674         // TODO: handle argument checking for primary axis location which
675
// should not be null
676
this.domainAxisLocations.set(index, location);
677         notifyListeners(new PlotChangeEvent(this));
678     }
679
680     /**
681      * Returns the domain axis edge. This is derived from the axis location
682      * and the plot orientation.
683      *
684      * @return The edge (never <code>null</code>).
685      */

686     public RectangleEdge getDomainAxisEdge() {
687         return getDomainAxisEdge(0);
688     }
689
690     /**
691      * Returns the edge for a domain axis.
692      *
693      * @param index the axis index.
694      *
695      * @return The edge (never <code>null</code>).
696      */

697     public RectangleEdge getDomainAxisEdge(int index) {
698         RectangleEdge result = null;
699         AxisLocation location = getDomainAxisLocation(index);
700         if (location != null) {
701             result = Plot.resolveDomainAxisLocation(location, this.orientation);
702         }
703         else {
704             result = RectangleEdge.opposite(getDomainAxisEdge(0));
705         }
706         return result;
707     }
708
709     /**
710      * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
711      * to all registered listeners.
712      */

713     public void clearDomainAxes() {
714         for (int i = 0; i < this.domainAxes.size(); i++) {
715             CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
716             if (axis != null) {
717                 axis.removeChangeListener(this);
718             }
719         }
720         this.domainAxes.clear();
721         notifyListeners(new PlotChangeEvent(this));
722     }
723
724     /**
725      * Configures the domain axes.
726      */

727     public void configureDomainAxes() {
728         for (int i = 0; i < this.domainAxes.size(); i++) {
729             CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i);
730             if (axis != null) {
731                 axis.configure();
732             }
733         }
734     }
735
736     /**
737      * Returns the range axis for the plot. If the range axis for this plot is
738      * null, then the method will return the parent plot's range axis (if there
739      * is a parent plot).
740      *
741      * @return The range axis (possibly <code>null</code>).
742      */

743     public ValueAxis getRangeAxis() {
744         return getRangeAxis(0);
745     }
746
747     /**
748      * Returns a range axis.
749      *
750      * @param index the axis index.
751      *
752      * @return The axis (<code>null</code> possible).
753      */

754     public ValueAxis getRangeAxis(int index) {
755         ValueAxis result = null;
756         if (index < this.rangeAxes.size()) {
757             result = (ValueAxis) this.rangeAxes.get(index);
758         }
759         if (result == null) {
760             Plot parent = getParent();
761             if (parent instanceof CategoryPlot) {
762                 CategoryPlot cp = (CategoryPlot) parent;
763                 result = cp.getRangeAxis(index);
764             }
765         }
766         return result;
767     }
768
769     /**
770      * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
771      * all registered listeners.
772      *
773      * @param axis the axis (<code>null</code> permitted).
774      */

775     public void setRangeAxis(ValueAxis axis) {
776         setRangeAxis(0, axis);
777     }
778
779     /**
780      * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
781      * listeners.
782      *
783      * @param index the axis index.
784      * @param axis the axis.
785      */

786     public void setRangeAxis(int index, ValueAxis axis) {
787         setRangeAxis(index, axis, true);
788     }
789         
790     /**
791      * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
792      * all registered listeners.
793      *
794      * @param index the axis index.
795      * @param axis the axis.
796      * @param notify notify listeners?
797      */

798     public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
799         ValueAxis existing = (ValueAxis) this.rangeAxes.get(index);
800         if (existing != null) {
801             existing.removeChangeListener(this);
802         }
803         if (axis != null) {
804             axis.setPlot(this);
805         }
806         this.rangeAxes.set(index, axis);
807         if (axis != null) {
808             axis.configure();
809             axis.addChangeListener(this);
810         }
811         if (notify) {
812             notifyListeners(new PlotChangeEvent(this));
813         }
814     }
815
816     /**
817      * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
818      * to all registered listeners.
819      *
820      * @param axes the axes.
821      */

822     public void setRangeAxes(ValueAxis[] axes) {
823         for (int i = 0; i < axes.length; i++) {
824             setRangeAxis(i, axes[i], false);
825         }
826         notifyListeners(new PlotChangeEvent(this));
827     }
828     
829     /**
830      * Returns the range axis location.
831      *
832      * @return The location (never <code>null</code>).
833      */

834     public AxisLocation getRangeAxisLocation() {
835         return getRangeAxisLocation(0);
836     }
837
838     /**
839      * Returns the location for a range axis.
840      *
841      * @param index the axis index.
842      *
843      * @return The location.
844      */

845     public AxisLocation getRangeAxisLocation(int index) {
846         AxisLocation result = null;
847         if (index < this.rangeAxisLocations.size()) {
848             result = (AxisLocation) this.rangeAxisLocations.get(index);
849         }
850         if (result == null) {
851             result = AxisLocation.getOpposite(getRangeAxisLocation(0));
852         }
853         return result;
854     }
855
856     /**
857      * Sets the location of the range axis and sends a {@link PlotChangeEvent}
858      * to all registered listeners.
859      *
860      * @param location the location (<code>null</code> not permitted).
861      */

862     public void setRangeAxisLocation(AxisLocation location) {
863         // defer argument checking...
864
setRangeAxisLocation(location, true);
865     }
866
867     /**
868      * Sets the location of the range axis and, if requested, sends a
869      * {@link PlotChangeEvent} to all registered listeners.
870      *
871      * @param location the location (<code>null</code> not permitted).
872      * @param notify notify listeners?
873      */

874     public void setRangeAxisLocation(AxisLocation location, boolean notify) {
875         setRangeAxisLocation(0, location, notify);
876     }
877
878     /**
879      * Sets the location for a range axis and sends a {@link PlotChangeEvent}
880      * to all registered listeners.
881      *
882      * @param index the axis index.
883      * @param location the location.
884      */

885     public void setRangeAxisLocation(int index, AxisLocation location) {
886         setRangeAxisLocation(index, location, true);
887     }
888
889     /**
890      * Sets the location for a range axis and sends a {@link PlotChangeEvent}
891      * to all registered listeners.
892      *
893      * @param index the axis index.
894      * @param location the location.
895      * @param notify notify listeners?
896      */

897     public void setRangeAxisLocation(int index, AxisLocation location,
898                                      boolean notify) {
899         // TODO: don't allow null for index = 0
900
this.rangeAxisLocations.set(index, location);
901         if (notify) {
902             notifyListeners(new PlotChangeEvent(this));
903         }
904     }
905
906     /**
907      * Returns the edge where the primary range axis is located.
908      *
909      * @return The edge (never <code>null</code>).
910      */

911     public RectangleEdge getRangeAxisEdge() {
912         return getRangeAxisEdge(0);
913     }
914
915     /**
916      *