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      * Returns the edge for a range axis.
917      *
918      * @param index the axis index.
919      *
920      * @return The edge.
921      */

922     public RectangleEdge getRangeAxisEdge(int index) {
923         AxisLocation location = getRangeAxisLocation(index);
924         RectangleEdge result = Plot.resolveRangeAxisLocation(
925             location, this.orientation
926         );
927         if (result == null) {
928             result = RectangleEdge.opposite(getRangeAxisEdge(0));
929         }
930         return result;
931     }
932
933     /**
934      * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
935      * to all registered listeners.
936      */

937     public void clearRangeAxes() {
938         for (int i = 0; i < this.rangeAxes.size(); i++) {
939             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
940             if (axis != null) {
941                 axis.removeChangeListener(this);
942             }
943         }
944         this.rangeAxes.clear();
945         notifyListeners(new PlotChangeEvent(this));
946     }
947
948     /**
949      * Configures the range axes.
950      */

951     public void configureRangeAxes() {
952         for (int i = 0; i < this.rangeAxes.size(); i++) {
953             ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
954             if (axis != null) {
955                 axis.configure();
956             }
957         }
958     }
959
960     /**
961      * Returns the primary dataset for the plot.
962      *
963      * @return The primary dataset (possibly <code>null</code>).
964      */

965     public CategoryDataset getDataset() {
966         return getDataset(0);
967     }
968
969     /**
970      * Returns the dataset at the given index.
971      *
972      * @param index the dataset index.
973      *
974      * @return The dataset (possibly <code>null</code>).
975      */

976     public CategoryDataset getDataset(int index) {
977         CategoryDataset result = null;
978         if (this.datasets.size() > index) {
979             result = (CategoryDataset) this.datasets.get(index);
980         }
981         return result;
982     }
983
984     /**
985      * Sets the dataset for the plot, replacing the existing dataset, if there
986      * is one. This method also calls the
987      * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
988      * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
989      * registered listeners.
990      *
991      * @param dataset the dataset (<code>null</code> permitted).
992      */

993     public void setDataset(CategoryDataset dataset) {
994         setDataset(0, dataset);
995     }
996
997     /**
998      * Sets a dataset for the plot.
999      *
1000     * @param index the dataset index.
1001     * @param dataset the dataset (<code>null</code> permitted).
1002     */

1003    public void setDataset(int index, CategoryDataset dataset) {
1004        
1005        CategoryDataset existing = (CategoryDataset) this.datasets.get(index);
1006        if (existing != null) {
1007            existing.removeChangeListener(this);
1008        }
1009        this.datasets.set(index, dataset);
1010        if (dataset != null) {
1011            dataset.addChangeListener(this);
1012        }
1013        
1014        // send a dataset change event to self...
1015
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1016        datasetChanged(event);
1017        
1018    }
1019
1020    /**
1021     * Maps a dataset to a particular domain axis.
1022     *
1023     * @param index the dataset index (zero-based).
1024     * @param axisIndex the axis index (zero-based).
1025     */

1026    public void mapDatasetToDomainAxis(int index, int axisIndex) {
1027        this.datasetToDomainAxisMap.set(index, new Integer JavaDoc(axisIndex));
1028        // fake a dataset change event to update axes...
1029
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1030    }
1031
1032    /**
1033     * Returns the domain axis for a dataset. You can change the axis for a
1034     * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
1035     *
1036     * @param index the dataset index.
1037     *
1038     * @return The domain axis.
1039     */

1040    public CategoryAxis getDomainAxisForDataset(int index) {
1041        CategoryAxis result = getDomainAxis();
1042        Integer JavaDoc axisIndex = (Integer JavaDoc) this.datasetToDomainAxisMap.get(index);
1043        if (axisIndex != null) {
1044            result = getDomainAxis(axisIndex.intValue());
1045        }
1046        return result;
1047    }
1048    
1049    /**
1050     * Maps a dataset to a particular range axis.
1051     *
1052     * @param index the dataset index (zero-based).
1053     * @param axisIndex the axis index (zero-based).
1054     */

1055    public void mapDatasetToRangeAxis(int index, int axisIndex) {
1056        this.datasetToRangeAxisMap.set(index, new Integer JavaDoc(axisIndex));
1057        // fake a dataset change event to update axes...
1058
datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1059    }
1060
1061    /**
1062     * Returns the range axis for a dataset. You can change the axis for a
1063     * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
1064     *
1065     * @param index the dataset index.
1066     *
1067     * @return The range axis.
1068     */

1069    public ValueAxis getRangeAxisForDataset(int index) {
1070        ValueAxis result = getRangeAxis();
1071        Integer JavaDoc axisIndex = (Integer JavaDoc) this.datasetToRangeAxisMap.get(index);
1072        if (axisIndex != null) {
1073            result = getRangeAxis(axisIndex.intValue());
1074        }
1075        return result;
1076    }
1077    
1078    /**
1079     * Returns a reference to the renderer for the plot.
1080     *
1081     * @return The renderer.
1082     */

1083    public CategoryItemRenderer getRenderer() {
1084        return getRenderer(0);
1085    }
1086
1087    /**
1088     * Returns the renderer at the given index.
1089     *
1090     * @param index the renderer index.
1091     *
1092     * @return The renderer (possibly <code>null</code>).
1093     */

1094    public CategoryItemRenderer getRenderer(int index) {
1095        CategoryItemRenderer result = null;
1096        if (this.renderers.size() > index) {
1097            result = (CategoryItemRenderer) this.renderers.get(index);
1098        }
1099        return result;
1100    }
1101    
1102    /**
1103     * Sets the renderer at index 0 (sometimes referred to as the "primary"
1104     * renderer) and sends a {@link PlotChangeEvent} to all registered
1105     * listeners.
1106     *
1107     * @param renderer the renderer (<code>null</code> permitted.
1108     */

1109    public void setRenderer(CategoryItemRenderer renderer) {
1110        setRenderer(0, renderer, true);
1111    }
1112
1113    /**
1114     * Sets the renderer at index 0 (sometimes referred to as the "primary"
1115     * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
1116     * registered listeners.
1117     * <p>
1118     * You can set the renderer to <code>null</code>, but this is not
1119     * recommended because:
1120     * <ul>
1121     * <li>no data will be displayed;</li>
1122     * <li>the plot background will not be painted;</li>
1123     * </ul>
1124     *
1125     * @param renderer the renderer (<code>null</code> permitted).
1126     * @param notify notify listeners?
1127     */

1128    public void setRenderer(CategoryItemRenderer renderer, boolean notify) {
1129        setRenderer(0, renderer, notify);
1130    }
1131
1132    /**
1133     * Sets the renderer at the specified index and sends a
1134     * {@link PlotChangeEvent} to all registered listeners.
1135     *
1136     * @param index the index.
1137     * @param renderer the renderer (<code>null</code> permitted).
1138     */

1139    public void setRenderer(int index, CategoryItemRenderer renderer) {
1140        setRenderer(index, renderer, true);
1141    }
1142
1143    /**
1144     * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered
1145     * listeners.
1146     *
1147     * @param index the index.
1148     * @param renderer the renderer (<code>null</code> permitted).
1149     * @param notify notify listeners?
1150     */

1151    public void setRenderer(int index, CategoryItemRenderer renderer,
1152                            boolean notify) {
1153        
1154        // stop listening to the existing renderer...
1155
CategoryItemRenderer existing
1156            = (CategoryItemRenderer) this.renderers.get(index);
1157        if (existing != null) {
1158            existing.removeChangeListener(this);
1159        }
1160        
1161        // register the new renderer...
1162
this.renderers.set(index, renderer);
1163        if (renderer != null) {
1164            renderer.setPlot(this);
1165            renderer.addChangeListener(this);
1166        }
1167        
1168        configureDomainAxes();
1169        configureRangeAxes();
1170        
1171        if (notify) {
1172            notifyListeners(new PlotChangeEvent(this));
1173        }
1174    }
1175
1176    /**
1177     * Returns the renderer for the specified dataset. If the dataset doesn't
1178     * belong to the plot, this method will return <code>null</code>.
1179     *
1180     * @param dataset the dataset (<code>null</code> permitted).
1181     *
1182     * @return The renderer (possibly <code>null</code>).
1183     */

1184    public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) {
1185        CategoryItemRenderer result = null;
1186        for (int i = 0; i < this.datasets.size(); i++) {
1187            if (this.datasets.get(i) == dataset) {
1188                result = (CategoryItemRenderer) this.renderers.get(i);
1189                break;
1190            }
1191        }
1192        return result;
1193    }
1194    
1195    /**
1196     * Returns the index of the specified renderer, or <code>-1</code> if the
1197     * renderer is not assigned to this plot.
1198     *
1199     * @param renderer the renderer (<code>null</code> permitted).
1200     *
1201     * @return The renderer index.
1202     */

1203    public int getIndexOf(CategoryItemRenderer renderer) {
1204        return this.renderers.indexOf(renderer);
1205    }
1206
1207    /**
1208     * Returns the dataset rendering order.
1209     *
1210     * @return The order (never <code>null</code>).
1211     */

1212    public DatasetRenderingOrder getDatasetRenderingOrder() {
1213        return this.renderingOrder;
1214    }
1215
1216    /**
1217     * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1218     * registered listeners. By default, the plot renders the primary dataset
1219     * last (so that the primary dataset overlays the secondary datasets). You
1220     * can reverse this if you want to.
1221     *
1222     * @param order the rendering order (<code>null</code> not permitted).
1223     */

1224    public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1225        if (order == null) {
1226            throw new IllegalArgumentException JavaDoc("Null 'order' argument.");
1227        }
1228        this.renderingOrder = order;
1229        notifyListeners(new PlotChangeEvent(this));
1230    }
1231
1232    /**
1233     * Returns the order in which the columns are rendered.
1234     *
1235     * @return The order.
1236     */

1237    public SortOrder getColumnRenderingOrder() {
1238        return this.columnRenderingOrder;
1239    }
1240    
1241    /**
1242     * Sets the order in which the columns should be rendered.
1243     *
1244     * @param order the order.
1245     */

1246    public void setColumnRenderingOrder(SortOrder order) {
1247        this.columnRenderingOrder = order;
1248    }
1249    
1250    /**
1251     * Returns the order in which the rows should be rendered.
1252     *
1253     * @return The order (never <code>null</code>).
1254     */

1255    public SortOrder getRowRenderingOrder() {
1256        return this.rowRenderingOrder;
1257    }
1258
1259    /**
1260     * Sets the order in which the rows should be rendered.
1261     *
1262     * @param order the order (<code>null</code> not allowed).
1263     */

1264    public void setRowRenderingOrder(SortOrder order) {
1265        if (order == null) {
1266            throw new IllegalArgumentException JavaDoc("Null 'order' argument.");
1267        }
1268        this.rowRenderingOrder = order;
1269    }
1270    
1271    /**
1272     * Returns the flag that controls whether the domain grid-lines are visible.
1273     *
1274     * @return The <code>true</code> or <code>false</code>.
1275     */

1276    public boolean isDomainGridlinesVisible() {
1277        return this.domainGridlinesVisible;
1278    }
1279
1280    /**
1281     * Sets the flag that controls whether or not grid-lines are drawn against
1282     * the domain axis.
1283     * <p>
1284     * If the flag value changes, a {@link PlotChangeEvent} is sent to all
1285     * registered listeners.
1286     *
1287     * @param visible the new value of the flag.
1288     */

1289    public void setDomainGridlinesVisible(boolean visible) {
1290        if (this.domainGridlinesVisible != visible) {
1291            this.domainGridlinesVisible = visible;
1292            notifyListeners(new PlotChangeEvent(this));
1293        }
1294    }
1295
1296    /**
1297     * Returns the position used for the domain gridlines.
1298     *
1299     * @return The gridline position.
1300     */

1301    public CategoryAnchor getDomainGridlinePosition() {
1302        return this.domainGridlinePosition;
1303    }
1304
1305    /**
1306     * Sets the position used for the domain gridlines.
1307     *
1308     * @param position the position.
1309     */

1310    public void setDomainGridlinePosition(CategoryAnchor position) {
1311        this.domainGridlinePosition = position;
1312        notifyListeners(new PlotChangeEvent(this));
1313    }
1314
1315    /**
1316     * Returns the stroke used to draw grid-lines against the domain axis.
1317     *
1318     * @return The stroke.
1319     */

1320    public Stroke JavaDoc getDomainGridlineStroke() {
1321        return this.domainGridlineStroke;
1322    }
1323
1324    /**
1325     * Sets the stroke used to draw grid-lines against the domain axis. A
1326     * {@link PlotChangeEvent} is sent to all registered listeners.
1327     *
1328     * @param stroke the stroke.
1329     */

1330    public void setDomainGridlineStroke(Stroke JavaDoc stroke) {
1331        this.domainGridlineStroke = stroke;
1332        notifyListeners(new PlotChangeEvent(this));
1333    }
1334
1335    /**
1336     * Returns the paint used to draw grid-lines against the domain axis.
1337     *
1338     * @return The paint.
1339     */

1340    public Paint JavaDoc getDomainGridlinePaint() {
1341        return this.domainGridlinePaint;
1342    }
1343
1344    /**
1345     * Sets the paint used to draw the grid-lines (if any) against the domain
1346     * axis. A {@link PlotChangeEvent} is sent to all registered listeners.
1347     *
1348     * @param paint the paint.
1349     */

1350    public void setDomainGridlinePaint(Paint JavaDoc paint) {
1351        this.domainGridlinePaint = paint;
1352        notifyListeners(new PlotChangeEvent(this));
1353    }
1354
1355    /**
1356     * Returns the flag that controls whether the range grid-lines are visible.
1357     *
1358     * @return The flag.
1359     */

1360    public boolean isRangeGridlinesVisible() {
1361        return this.rangeGridlinesVisible;
1362    }
1363
1364    /**
1365     * Sets the flag that controls whether or not grid-lines are drawn against
1366     * the range axis. If the flag changes value, a {@link PlotChangeEvent} is
1367     * sent to all registered listeners.
1368     *
1369     * @param visible the new value of the flag.
1370     */

1371    public void setRangeGridlinesVisible(boolean visible) {
1372        if (this.rangeGridlinesVisible != visible) {
1373            this.rangeGridlinesVisible = visible;
1374            notifyListeners(new PlotChangeEvent(this));
1375        }
1376    }
1377
1378    /**
1379     * Returns the stroke used to draw the grid-lines against the range axis.
1380     *
1381     * @return The stroke.
1382     */

1383    public Stroke JavaDoc getRangeGridlineStroke() {
1384        return this.rangeGridlineStroke;
1385    }
1386
1387    /**
1388     * Sets the stroke used to draw the grid-lines against the range axis.
1389     * A {@link PlotChangeEvent} is sent to all registered listeners.
1390     *
1391     * @param stroke the stroke.
1392     */

1393    public void setRangeGridlineStroke(Stroke JavaDoc stroke) {
1394        this.rangeGridlineStroke = stroke;
1395        notifyListeners(new PlotChangeEvent(this));
1396    }
1397
1398    /**
1399     * Returns the paint used to draw the grid-lines against the range axis.
1400     *
1401     * @return The paint.
1402     */

1403    public Paint JavaDoc getRangeGridlinePaint() {
1404        return this.rangeGridlinePaint;
1405    }
1406
1407    /**
1408     * Sets the paint used to draw the grid lines against the range axis.
1409     * A {@link PlotChangeEvent} is sent to all registered listeners.
1410     *
1411     * @param paint the paint.
1412     */

1413    public void setRangeGridlinePaint(Paint JavaDoc paint) {
1414        this.rangeGridlinePaint = paint;
1415        notifyListeners(new PlotChangeEvent(this));
1416    }
1417    
1418    /**
1419     * Returns the fixed legend items, if any.
1420     *
1421     * @return The legend items (possibly <code>null</code>).
1422     */

1423    public LegendItemCollection getFixedLegendItems() {
1424        return this.fixedLegendItems;
1425    }
1426
1427    /**
1428     * Sets the fixed legend items for the plot. Leave this set to
1429     * <code>null</code> if you prefer the legend items to be created
1430     * automatically.
1431     *
1432     * @param items the legend items (<code>null</code> permitted).
1433     */

1434    public void setFixedLegendItems(LegendItemCollection items) {
1435        this.fixedLegendItems = items;
1436        notifyListeners(new PlotChangeEvent(this));
1437    }
1438    
1439    /**
1440     * Returns the legend items for the plot. By default, this method creates
1441     * a legend item for each series in each of the datasets. You can change
1442     * this behaviour by overriding this method.
1443     *
1444     * @return The legend items.
1445     */

1446    public LegendItemCollection getLegendItems() {
1447        LegendItemCollection result = this.fixedLegendItems;
1448        if (result == null) {
1449            result = new LegendItemCollection();
1450            // get the legend items for the datasets...
1451
int count = this.datasets.size();
1452            for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
1453                CategoryDataset dataset = getDataset(datasetIndex);
1454                if (dataset != null) {
1455                    CategoryItemRenderer renderer = getRenderer(datasetIndex);
1456                    if (renderer != null) {
1457                        int seriesCount = dataset.getRowCount();
1458                        for (int i = 0; i < seriesCount; i++) {
1459                            LegendItem item = renderer.getLegendItem(
1460                                datasetIndex, i
1461                            );
1462                            if (item != null) {
1463                                result.add(item);
1464                            }
1465                        }
1466                    }
1467                }
1468            }
1469        }
1470        return result;
1471    }
1472
1473    /**
1474     * Handles a 'click' on the plot by updating the anchor value.
1475     *
1476     * @param x x-coordinate of the click (in Java2D space).
1477     * @param y y-coordinate of the click (in Java2D space).
1478     * @param info information about the plot's dimensions.
1479     *
1480     */

1481    public void handleClick(int x, int y, PlotRenderingInfo info) {
1482
1483        Rectangle2D JavaDoc dataArea = info.getDataArea();
1484        if (dataArea.contains(x, y)) {
1485            // set the anchor value for the range axis...
1486
double java2D = 0.0;
1487            if (this.orientation == PlotOrientation.HORIZONTAL) {
1488                java2D = x;
1489            }
1490            else if (this.orientation == PlotOrientation.VERTICAL) {
1491                java2D = y;
1492            }
1493            RectangleEdge edge = Plot.resolveRangeAxisLocation(
1494                getRangeAxisLocation(), this.orientation
1495            );
1496            double value = getRangeAxis().java2DToValue(
1497                java2D, info.getDataArea(), edge
1498            );
1499            setAnchorValue(value);
1500            setRangeCrosshairValue(value);
1501        }
1502
1503    }
1504
1505    /**
1506     * Zooms (in or out) on the plot's value axis.
1507     * <p>
1508     * If the value 0.0 is passed in as the zoom percent, the auto-range
1509     * calculation for the axis is restored (which sets the range to include
1510     * the minimum and maximum data values, thus displaying all the data).
1511     *
1512     * @param percent the zoom amount.
1513     */

1514    public void zoom(double percent) {
1515
1516        if (percent > 0.0) {
1517            double range = getRangeAxis().getRange().getLength();
1518            double scaledRange = range * percent;
1519            getRangeAxis().setRange(
1520                this.anchorValue - scaledRange / 2.0,
1521                this.anchorValue + scaledRange / 2.0
1522            );
1523        }
1524        else {
1525            getRangeAxis().setAutoRange(true);
1526        }
1527
1528    }
1529
1530    /**
1531     * Receives notification of a change to the plot's dataset.
1532     * <P>
1533     * The range axis bounds will be recalculated if necessary.
1534     *
1535     * @param event information about the event (not used here).
1536     */

1537    public void datasetChanged(DatasetChangeEvent event) {
1538
1539        int count = this.rangeAxes.size();
1540        for (int axisIndex = 0; axisIndex < count; axisIndex++) {
1541            ValueAxis yAxis = getRangeAxis(axisIndex);
1542            if (yAxis != null) {
1543                yAxis.configure();
1544            }
1545        }
1546        if (getParent() != null) {
1547            getParent().datasetChanged(event);
1548        }
1549        else {
1550            PlotChangeEvent e = new PlotChangeEvent(this);
1551            e.setType(ChartChangeEventType.DATASET_UPDATED);
1552            notifyListeners(e);
1553        }
1554
1555    }
1556
1557    /**
1558     * Receives notification of a renderer change event.
1559     *
1560     * @param event the event.
1561     */

1562    public void rendererChanged(RendererChangeEvent event) {
1563        Plot parent = getParent();
1564        if (parent != null) {
1565            if (parent instanceof RendererChangeListener) {
1566                RendererChangeListener rcl = (RendererChangeListener) parent;
1567                rcl.rendererChanged(event);
1568            }
1569            else {
1570                // this should never happen with the existing code, but throw
1571
// an exception in case future changes make it possible...
1572
throw new RuntimeException JavaDoc(
1573                    "The renderer has changed and I don't know what to do!"
1574                );
1575            }
1576        }
1577        else {
1578            PlotChangeEvent e = new PlotChangeEvent(this);
1579            notifyListeners(e);
1580        }
1581    }
1582    
1583    /**
1584     * Adds a marker for display (in the foreground) against the domain axis and
1585     * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
1586     * marker will be drawn by the renderer as a line perpendicular to the
1587     * domain axis, however this is entirely up to the renderer.
1588     *
1589     * @param marker the marker (<code>null</code> not permitted).
1590     */

1591    public void addDomainMarker(CategoryMarker marker) {
1592        addDomainMarker(marker, Layer.FOREGROUND);
1593    }
1594        
1595    /**
1596     * Adds a marker for display against the domain axis and sends a
1597     * {@link PlotChangeEvent} to all registered listeners. Typically a marker
1598     * will be drawn by the renderer as a line perpendicular to the domain axis,
1599     * however this is entirely up to the renderer.
1600     *
1601     * @param marker the marker (<code>null</code> not permitted).
1602     * @param layer the layer (foreground or background) (<code>null</code>
1603     * not permitted).
1604     */

1605    public void addDomainMarker(CategoryMarker marker, Layer layer) {
1606        addDomainMarker(0, marker, layer);
1607    }
1608
1609    /**
1610     * Adds a marker for display by a particular renderer.
1611     * <P>
1612     * Typically a marker will be drawn by the renderer as a line perpendicular
1613     * to a domain axis, however this is entirely up to the renderer.
1614     *
1615     * @param index the renderer index.
1616     * @param marker the marker.
1617     * @param layer the layer.
1618     */

1619    public void addDomainMarker(int index, CategoryMarker marker, Layer layer) {
1620        Collection JavaDoc markers;
1621        if (layer == Layer.FOREGROUND) {
1622            markers = (Collection JavaDoc) this.foregroundDomainMarkers.get(
1623                new Integer JavaDoc(index)
1624            );
1625            if (markers == null) {
1626                markers = new java.util.ArrayList JavaDoc();
1627                this.foregroundDomainMarkers.put(new Integer JavaDoc(index), markers);
1628            }
1629            markers.add(marker);
1630        }
1631        else if (layer == Layer.BACKGROUND) {
1632            markers = (Collection JavaDoc) this.backgroundDomainMarkers.get(
1633                new Integer JavaDoc(index)
1634            );
1635            if (markers == null) {
1636                markers = new java.util.ArrayList JavaDoc();
1637                this.backgroundDomainMarkers.put(new Integer JavaDoc(index), markers);
1638            }
1639            markers.add(marker);
1640        }
1641        notifyListeners(new PlotChangeEvent(this));
1642    }
1643
1644    /**
1645     * Clears all the domain markers for the plot and sends a
1646     * {@link PlotChangeEvent} to all registered listeners.
1647     */

1648    public void clearDomainMarkers() {
1649        if (this.backgroundDomainMarkers != null) {
1650            this.backgroundDomainMarkers.clear();
1651        }
1652        if (this.foregroundDomainMarkers != null) {
1653            this.foregroundDomainMarkers.clear();
1654        }
1655        notifyListeners(new PlotChangeEvent(this));
1656    }
1657
1658    /**
1659     * Returns the list of domain markers (read only) for the specified layer.
1660     *
1661     * @param layer the layer (foreground or background).
1662     *
1663     * @return The list of domain markers.
1664     */

1665    public Collection JavaDoc getDomainMarkers(Layer layer) {
1666        return getDomainMarkers(0, layer);
1667    }
1668
1669    /**
1670     * Returns a collection of domain markers for a particular renderer and
1671     * layer.
1672     *
1673     * @param index the renderer index.
1674     * @param layer the layer.
1675     *
1676     * @return A collection of markers (possibly <code>null</code>).
1677     */

1678    public Collection JavaDoc getDomainMarkers(int index, Layer layer) {
1679        Collection JavaDoc result = null;
1680        Integer JavaDoc key = new Integer JavaDoc(index);
1681        if (layer == Layer.FOREGROUND) {
1682            result = (Collection JavaDoc) this.foregroundDomainMarkers.get(key);
1683        }
1684        else if (layer == Layer.BACKGROUND) {
1685            result = (Collection JavaDoc) this.backgroundDomainMarkers.get(key);
1686        }
1687        if (result != null) {
1688            result = Collections.unmodifiableCollection(result);
1689        }
1690        return result;
1691    }
1692    
1693    /**
1694     * Clears all the domain markers for the specified renderer.
1695     *
1696     * @param index the renderer index.
1697     */

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

1725    public void addRangeMarker(Marker marker) {
1726        addRangeMarker(marker, Layer.FOREGROUND);
1727    }
1728        
1729    /**
1730     * Adds a marker for display against the range axis and sends a
1731     * {@link PlotChangeEvent} to all registered listeners. Typically a marker
1732     * will be drawn by the renderer as a line perpendicular to the range axis,
1733     * however this is entirely up to the renderer.
1734     *
1735     * @param marker the marker (<code>null</code> not permitted).
1736     * @param layer the layer (foreground or background) (<code>null</code>
1737     * not permitted).
1738     */

1739    public void addRangeMarker(Marker marker, Layer layer) {
1740        addRangeMarker(0, marker, layer);
1741    }
1742
1743    /**
1744     * Adds a marker for display by a particular renderer.
1745     * <P>
1746     * Typically a marker will be drawn by the renderer as a line perpendicular
1747     * to a range axis, however this is entirely up to the renderer.
1748     *
1749     * @param index the renderer index.
1750     * @param marker the marker.
1751     * @param layer the layer.
1752     */

1753    public void addRangeMarker(int index, Marker marker, Layer layer) {
1754        Collection JavaDoc markers;
1755        if (layer == Layer.FOREGROUND) {
1756            markers = (Collection JavaDoc) this.foregroundRangeMarkers.get(
1757                new Integer JavaDoc(index)
1758            );
1759            if (markers == null) {
1760                markers = new java.util.ArrayList JavaDoc();
1761                this.foregroundRangeMarkers.put(new Integer JavaDoc(index), markers);
1762            }
1763            markers.add(marker);
1764        }
1765        else if (layer == Layer.BACKGROUND) {
1766            markers = (Collection JavaDoc) this.backgroundRangeMarkers.get(
1767                new Integer JavaDoc(index)
1768            );
1769            if (markers == null) {
1770                markers = new java.util.ArrayList JavaDoc();
1771                this.backgroundRangeMarkers.put(new Integer JavaDoc(index), markers);
1772            }
1773            markers.add(marker);
1774        }
1775        notifyListeners(new PlotChangeEvent(this));
1776    }
1777
1778    /**
1779     * Clears all the range markers for the plot and sends a
1780     * {@link PlotChangeEvent} to all registered listeners.
1781     */

1782    public void clearRangeMarkers() {
1783        if (this.backgroundRangeMarkers != null) {
1784            this.backgroundRangeMarkers.clear();
1785        }
1786        if (this.foregroundRangeMarkers != null) {
1787            this.foregroundRangeMarkers.clear();
1788        }
1789        notifyListeners(new PlotChangeEvent(this));
1790    }
1791
1792    /**
1793     * Returns the list of range markers (read only) for the specified layer.
1794     *
1795     * @param layer the layer (foreground or background).
1796     *
1797     * @return The list of range markers.
1798     */

1799    public Collection JavaDoc getRangeMarkers(Layer layer) {
1800        return getRangeMarkers(0, layer);
1801    }
1802
1803    /**
1804     * Returns a collection of range markers for a particular renderer and
1805     * layer.
1806     *
1807     * @param index the renderer index.
1808     * @param layer the layer.
1809     *
1810     * @return A collection of markers (possibly <code>null</code>).
1811     */

1812    public Collection JavaDoc getRangeMarkers(int index, Layer layer) {
1813        Collection JavaDoc result = null;
1814        Integer JavaDoc key = new Integer JavaDoc(index);
1815        if (layer == Layer.FOREGROUND) {
1816            result = (Collection JavaDoc) this.foregroundRangeMarkers.get(key);
1817        }
1818        else if (layer == Layer.BACKGROUND) {
1819            result = (Collection JavaDoc) this.backgroundRangeMarkers.get(key);
1820        }
1821        if (result != null) {
1822            result = Collections.unmodifiableCollection(result);
1823        }
1824        return result;
1825    }
1826    
1827    /**
1828     * Clears all the range markers for the specified 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     * Returns a flag indicating whether or not the range crosshair is visible.
1853     *
1854     * @return The flag.
1855     */

1856    public boolean isRangeCrosshairVisible() {
1857        return this.rangeCrosshairVisible;
1858    }
1859
1860    /**
1861     * Sets the flag indicating whether or not the range crosshair is visible.
1862     *
1863     * @param flag the new value of the flag.
1864     */

1865    public void setRangeCrosshairVisible(boolean flag) {
1866
1867        if (this.rangeCrosshairVisible != flag) {
1868            this.rangeCrosshairVisible = flag;
1869            notifyListeners(new PlotChangeEvent(this));
1870        }
1871
1872    }
1873
1874    /**
1875     * Returns a flag indicating whether or not the crosshair should "lock-on"
1876     * to actual data values.
1877     *
1878     * @return The flag.
1879     */

1880    public boolean isRangeCrosshairLockedOnData() {
1881        return this.rangeCrosshairLockedOnData;
1882    }
1883
1884    /**
1885     * Sets the flag indicating whether or not the range crosshair should
1886     * "lock-on" to actual data values.
1887     *
1888     * @param flag the flag.
1889     */

1890    public void setRangeCrosshairLockedOnData(boolean flag) {
1891
1892        if (this.rangeCrosshairLockedOnData != flag) {
1893            this.rangeCrosshairLockedOnData = flag;
1894            notifyListeners(new PlotChangeEvent(this));
1895        }
1896
1897    }
1898
1899    /**
1900     * Returns the range crosshair value.
1901     *
1902     * @return The value.
1903     */

1904    public double getRangeCrosshairValue() {
1905        return this.rangeCrosshairValue;
1906    }
1907
1908    /**
1909     * Sets the domain crosshair value.
1910     * <P>
1911     * Registered listeners are notified that the plot has been modified, but
1912     * only if the crosshair is visible.
1913     *
1914     * @param value the new value.
1915     */

1916    public void setRangeCrosshairValue(double value) {
1917
1918        setRangeCrosshairValue(value, true);
1919
1920    }
1921
1922    /**
1923     * Sets the range crosshair value.
1924     * <P>
1925     * Registered listeners are notified that the axis has been modified, but
1926     * only if the crosshair is visible.
1927     *
1928     * @param value the new value.
1929     * @param notify a flag that controls whether or not listeners are
1930     * notified.
1931     */

1932    public void setRangeCrosshairValue(double value, boolean notify) {
1933
1934        this.rangeCrosshairValue = value;
1935        if (isRangeCrosshairVisible() && notify) {
1936            notifyListeners(new PlotChangeEvent(this));
1937        }
1938
1939    }
1940
1941    /**
1942     * Returns the pen-style (<code>Stroke</code>) used to draw the crosshair
1943     * (if visible).
1944     *
1945     * @return The crosshair stroke.
1946     */

1947    public Stroke JavaDoc getRangeCrosshairStroke() {
1948        return this.rangeCrosshairStroke;
1949    }
1950
1951    /**
1952     * Sets the pen-style (<code>Stroke</code>) used to draw the crosshairs
1953     * (if visible). A {@link PlotChangeEvent} is sent to all registered
1954     * listeners.
1955     *
1956     * @param stroke the new crosshair stroke.
1957     */

1958    public void setRangeCrosshairStroke(Stroke JavaDoc stroke) {
1959        this.rangeCrosshairStroke = stroke;
1960        notifyListeners(new PlotChangeEvent(this));
1961    }
1962
1963    /**
1964     * Returns the range crosshair color.
1965     *
1966     * @return The crosshair color.
1967     */

1968    public Paint JavaDoc getRangeCrosshairPaint() {
1969        return this.rangeCrosshairPaint;
1970    }
1971
1972    /**
1973     * Sets the Paint used to color the crosshairs (if visible) and notifies
1974     * registered listeners that the axis has been modified.
1975     *
1976     * @param paint the new crosshair paint.
1977     */

1978    public void setRangeCrosshairPaint(Paint JavaDoc paint) {
1979        this.rangeCrosshairPaint = paint;
1980        notifyListeners(new PlotChangeEvent(this));
1981    }
1982
1983    /**
1984     * Returns the list of annotations.
1985     *
1986     * @return The list of annotations.
1987     */

1988    public List JavaDoc getAnnotations() {
1989        return this.annotations;
1990    }
1991
1992    /**
1993     * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
1994     * registered listeners.
1995     *
1996     * @param annotation the annotation (<code>null</code> not permitted).
1997     */

1998    public void addAnnotation(CategoryAnnotation annotation) {
1999        if (annotation == null) {
2000            throw new IllegalArgumentException JavaDoc("Null 'annotation' argument.");
2001        }
2002        this.annotations.add(annotation);
2003        notifyListeners(new PlotChangeEvent(this));
2004    }
2005
2006    /**
2007     * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2008     * to all registered listeners.
2009     *
2010     * @param annotation the annotation (<code>null</code> not permitted).
2011     *
2012     * @return A boolean (indicates whether or not the annotation was removed).
2013     */

2014    public boolean removeAnnotation(CategoryAnnotation annotation) {
2015        if (annotation == null) {
2016            throw new IllegalArgumentException JavaDoc("Null 'annotation' argument.");
2017        }
2018        boolean removed = this.annotations.remove(annotation);
2019        if (removed) {
2020            notifyListeners(new PlotChangeEvent(this));
2021        }
2022        return removed;
2023    }
2024
2025    /**
2026     * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2027     * registered listeners.
2028     */

2029    public void clearAnnotations() {
2030        this.annotations.clear();
2031        notifyListeners(new PlotChangeEvent(this));
2032    }
2033
2034    /**
2035     * Calculates the space required for the domain axis/axes.
2036     *
2037     * @param g2 the graphics device.
2038     * @param plotArea the plot area.
2039     * @param space a carrier for the result (<code>null</code> permitted).
2040     *
2041     * @return The required space.
2042     */

2043    protected AxisSpace calculateDomainAxisSpace(Graphics2D JavaDoc g2,
2044                                                 Rectangle2D JavaDoc plotArea,
2045                                                 AxisSpace space) {
2046                                                     
2047        if (space == null) {
2048            space = new AxisSpace();
2049        }
2050        
2051        // reserve some space for the domain axis...
2052
if (this.fixedDomainAxisSpace != null) {
2053            if (this.orientation == PlotOrientation.HORIZONTAL) {
2054                space.ensureAtLeast(
2055                    this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT
2056                );
2057                space.ensureAtLeast(
2058                    this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT
2059                );
2060            }
2061            else if (this.orientation == PlotOrientation.VERTICAL) {
2062                space.ensureAtLeast(
2063                    this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP
2064                );
2065                space.ensureAtLeast(
2066                    this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM
2067                );
2068            }
2069        }
2070        else {
2071            // reserve space for the primary domain axis...
2072
RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
2073                getDomainAxisLocation(), this.orientation
2074            );
2075            if (this.drawSharedDomainAxis) {
2076                space = getDomainAxis().reserveSpace(
2077                    g2, this, plotArea, domainEdge, space
2078                );
2079            }
2080            
2081            // reserve space for any domain axes...
2082
for (int i = 0; i < this.domainAxes.size(); i++) {
2083                Axis xAxis = (Axis) this.domainAxes.get(i);
2084                if (xAxis != null) {
2085                    RectangleEdge edge = getDomainAxisEdge(i);
2086                    space = xAxis.reserveSpace(g2, this, plotArea, edge, space);
2087                }
2088            }
2089        }
2090
2091        return space;
2092                                                     
2093    }
2094    
2095    /**
2096     * Calculates the space required for the range axis/axes.
2097     *
2098     * @param g2 the graphics device.
2099     * @param plotArea the plot area.
2100     * @param space a carrier for the result (<code>null</code> permitted).
2101     *
2102     * @return The required space.
2103     */

2104    protected AxisSpace calculateRangeAxisSpace(Graphics2D JavaDoc g2,
2105                                                Rectangle2D JavaDoc plotArea,
2106                                                AxisSpace space) {
2107                                                  
2108        if (space == null) {
2109            space = new AxisSpace();
2110        }
2111        
2112        // reserve some space for the range axis...
2113
if (this.fixedRangeAxisSpace != null) {
2114            if (this.orientation == PlotOrientation.HORIZONTAL) {
2115                space.ensureAtLeast(
2116                    this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP
2117                );
2118                space.ensureAtLeast(
2119                    this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM
2120                );
2121            }
2122            else if (this.orientation == PlotOrientation.VERTICAL) {
2123                space.ensureAtLeast(
2124                    this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT
2125                );
2126                space.ensureAtLeast(
2127                    this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT
2128                );
2129            }
2130        }
2131        else {
2132            // reserve space for the range axes (if any)...
2133
for (int i = 0; i < this.rangeAxes.size(); i++) {
2134                Axis yAxis = (Axis) this.rangeAxes.get(i);
2135                if (yAxis != null) {
2136                    RectangleEdge edge = getRangeAxisEdge(i);
2137                    space = yAxis.reserveSpace(g2, this, plotArea, edge, space);
2138                }
2139            }
2140        }
2141        return space;
2142                                                    
2143    }
2144
2145
2146    /**
2147     * Calculates the space required for the axes.
2148     *
2149     * @param g2 the graphics device.
2150     * @param plotArea the plot area.
2151     *
2152     * @return The space required for the axes.
2153     */

2154    protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
2155                                           Rectangle2D JavaDoc plotArea) {
2156        AxisSpace space = new AxisSpace();
2157        space = calculateRangeAxisSpace(g2, plotArea, space);
2158        space = calculateDomainAxisSpace(g2, plotArea, space);
2159        return space;
2160    }
2161    
2162    /**
2163     * Draws the plot on a Java 2D graphics device (such as the screen or a
2164     * printer).
2165     * <P>
2166     * At your option, you may supply an instance of {@link PlotRenderingInfo}.
2167     * If you do, it will be populated with information about the drawing,
2168     * including various plot dimensions and tooltip info.
2169     *
2170     * @param g2 the graphics device.
2171     * @param area the area within which the plot (including axes) should
2172     * be drawn.
2173     * @param anchor the anchor point (<code>null</code> permitted).
2174     * @param parentState the state from the parent plot, if there is one.
2175     * @param state collects info as the chart is drawn (possibly
2176     * <code>null</code>).
2177     */

2178    public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area,
2179                     Point2D JavaDoc anchor,
2180                     PlotState parentState,
2181                     PlotRenderingInfo state) {
2182
2183        // if the plot area is too small, just return...
2184
boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2185        boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2186        if (b1 || b2) {
2187            return;
2188        }
2189
2190        // record the plot area...
2191
if (state != null) {
2192            state.setPlotArea(area);
2193        }
2194
2195        // adjust the drawing area for the plot insets (if any)...
2196
RectangleInsets insets = getInsets();
2197        insets.trim(area);
2198
2199        // calculate the data area...
2200
AxisSpace space = calculateAxisSpace(g2, area);
2201        Rectangle2D JavaDoc dataArea = space.shrink(area, null);
2202        this.axisOffset.trim(dataArea);
2203
2204        if (state != null) {
2205            state.setDataArea(dataArea);
2206        }
2207
2208        // if there is a renderer, it draws the background, otherwise use the
2209
// default background...
2210
if (getRenderer() != null) {
2211            getRenderer().drawBackground(g2, this, dataArea);
2212        }
2213        else {
2214            drawBackground(g2, dataArea);
2215        }
2216       
2217        Map JavaDoc axisStateMap = drawAxes(g2, area, dataArea, state);
2218
2219        drawDomainGridlines(g2, dataArea);
2220
2221        AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis());
2222        if (rangeAxisState == null) {
2223            if (parentState != null) {
2224                rangeAxisState
2225                    = (AxisState) parentState.getSharedAxisStates().get(
2226                        getRangeAxis()
2227                    );
2228            }
2229        }
2230        if (rangeAxisState != null) {
2231            drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2232        }
2233        
2234        // draw the markers...
2235
for (int i = 0; i < this.renderers.size(); i++) {
2236            drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2237        }
2238        for (int i = 0; i < this.renderers.size(); i++) {
2239            drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2240        }
2241
2242        // now render data items...
2243
boolean foundData = false;
2244        Shape JavaDoc savedClip = g2.getClip();
2245        g2.clip(dataArea);
2246        // set up the alpha-transparency...
2247
Composite JavaDoc originalComposite = g2.getComposite();
2248        g2.setComposite(AlphaComposite.getInstance(
2249            AlphaComposite.SRC_OVER, getForegroundAlpha())
2250        );
2251
2252        DatasetRenderingOrder order = getDatasetRenderingOrder();
2253        if (order == DatasetRenderingOrder.FORWARD) {
2254            for (int i = 0; i < this.datasets.size(); i++) {
2255                foundData = render(g2, dataArea, i, state) || foundData;
2256            }
2257        }
2258        else { // DatasetRenderingOrder.REVERSE
2259
for (int i = this.datasets.size() - 1; i >= 0; i--) {
2260                foundData = render(g2, dataArea, i, state) || foundData;
2261            }
2262        }
2263        g2.setClip(savedClip);
2264        g2.setComposite(originalComposite);
2265
2266        if (!foundData) {
2267            drawNoDataMessage(g2, dataArea);
2268        }
2269
2270        // draw vertical crosshair if required...
2271
if (isRangeCrosshairVisible()) {
2272            drawRangeLine(
2273                g2, dataArea, getRangeCrosshairValue(),
2274                getRangeCrosshairStroke(), getRangeCrosshairPaint()
2275            );
2276        }
2277
2278        // draw the foreground markers...
2279
for (int i = 0; i < this.renderers.size(); i++) {
2280            drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2281        }
2282        for (int i = 0; i < this.renderers.size(); i++) {
2283            drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2284        }
2285
2286        // draw the annotations (if any)...
2287
drawAnnotations(g2, dataArea);
2288
2289        // draw an outline around the plot area...
2290
if (getRenderer() != null) {
2291            getRenderer().drawOutline(g2, this, dataArea);
2292        }
2293        else {
2294            drawOutline(g2, dataArea);
2295        }
2296
2297    }
2298
2299    /**
2300     * A utility method for drawing the plot's axes.
2301     *
2302     * @param g2 the graphics device.
2303     * @param plotArea the plot area.
2304     * @param dataArea the data area.
2305     * @param plotState collects information about the plot (<code>null</code>
2306     * permitted).
2307     *
2308     * @return A map containing the axis states.
2309     */

2310    protected Map JavaDoc drawAxes(Graphics2D JavaDoc g2,
2311                           Rectangle2D JavaDoc plotArea,
2312                           Rectangle2D JavaDoc dataArea,
2313                           PlotRenderingInfo plotState) {
2314
2315        AxisCollection axisCollection = new AxisCollection();
2316
2317        // add domain axes to lists...
2318
for (int index = 0; index < this.domainAxes.size(); index++) {
2319            CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(index);
2320            if (xAxis != null) {
2321                axisCollection.add(xAxis, getDomainAxisEdge(index));
2322            }
2323        }
2324
2325        // add range axes to lists...
2326
for (int index = 0; index < this.rangeAxes.size(); index++) {
2327            ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index);
2328            if (yAxis != null) {
2329                axisCollection.add(yAxis, getRangeAxisEdge(index));
2330            }
2331        }
2332
2333        Map JavaDoc axisStateMap = new HashMap JavaDoc();
2334        
2335        // draw the top axes
2336
double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset(
2337            dataArea.getHeight()
2338        );
2339        Iterator JavaDoc iterator = axisCollection.getAxesAtTop().iterator();
2340        while (iterator.hasNext()) {
2341            Axis axis = (Axis) iterator.next();
2342            if (axis != null) {
2343                AxisState axisState = axis.draw(
2344                    g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState
2345                );
2346                cursor = axisState.getCursor();
2347                axisStateMap.put(axis, axisState);
2348            }
2349        }
2350
2351        // draw the bottom axes
2352
cursor = dataArea.getMaxY()
2353                 + this.axisOffset.calculateBottomOutset(dataArea.getHeight());
2354        iterator = axisCollection.getAxesAtBottom().iterator();
2355        while (iterator.hasNext()) {
2356            Axis axis = (Axis) iterator.next();
2357            if (axis != null) {
2358                AxisState axisState = axis.draw(
2359                    g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM,
2360                    plotState
2361                );
2362                cursor = axisState.getCursor();
2363                axisStateMap.put(axis, axisState);
2364            }
2365        }
2366
2367        // draw the left axes
2368
cursor = dataArea.getMinX()
2369                 - this.axisOffset.calculateLeftOutset(dataArea.getWidth());
2370        iterator = axisCollection.getAxesAtLeft().iterator();
2371        while (iterator.hasNext()) {
2372            Axis axis = (Axis) iterator.next();
2373            if (axis != null) {
2374                AxisState axisState = axis.draw(
2375                    g2, cursor, plotArea, dataArea, RectangleEdge.LEFT,
2376                    plotState
2377                );
2378                cursor = axisState.getCursor();
2379                axisStateMap.put(axis, axisState);
2380            }
2381        }
2382
2383        // draw the right axes
2384
cursor = dataArea.getMaxX()
2385                 + this.axisOffset.calculateRightOutset(dataArea.getWidth());
2386        iterator = axisCollection.getAxesAtRight().iterator();
2387        while (iterator.hasNext()) {
2388            Axis axis = (Axis) iterator.next();
2389            if (axis != null) {
2390                AxisState axisState = axis.draw(
2391                    g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT,
2392                    plotState
2393                );
2394                cursor = axisState.getCursor();
2395                axisStateMap.put(axis, axisState);
2396            }
2397        }
2398        
2399        return axisStateMap;
2400        
2401    }
2402
2403    /**
2404     * Draws a representation of a dataset within the dataArea region using the
2405     * appropriate renderer.
2406     *
2407     * @param g2 the graphics device.
2408     * @param dataArea the region in which the data is to be drawn.
2409     * @param index the dataset and renderer index.
2410     * @param info an optional object for collection dimension information.
2411     *
2412     * @return A boolean that indicates whether or not real data was found.
2413     */

2414    public boolean render(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea, int index,
2415                          PlotRenderingInfo info) {
2416
2417        boolean foundData = false;
2418        CategoryDataset currentDataset = getDataset(index);
2419        CategoryItemRenderer renderer = getRenderer(index);
2420        CategoryAxis domainAxis = getDomainAxisForDataset(index);
2421        ValueAxis rangeAxis = getRangeAxisForDataset(index);
2422        boolean hasData = !DatasetUtilities.isEmptyOrNull(currentDataset);
2423        if (hasData && renderer != null) {
2424            
2425            foundData = true;
2426            CategoryItemRendererState state = renderer.initialise(
2427                g2, dataArea, this, index, info
2428            );
2429            int columnCount = currentDataset.getColumnCount();
2430            int rowCount = currentDataset.getRowCount();
2431            int passCount = renderer.getPassCount();
2432            for (int pass = 0; pass < passCount; pass++) {
2433                if (this.columnRenderingOrder == SortOrder.ASCENDING) {
2434                    for (int column = 0; column < columnCount; column++) {
2435                        if (this.rowRenderingOrder == SortOrder.ASCENDING) {
2436                            for (int row = 0; row < rowCount; row++) {
2437                                renderer.drawItem(
2438                                    g2, state, dataArea, this, domainAxis,
2439                                    rangeAxis, currentDataset, row, column, pass
2440                                );
2441                            }
2442                        }
2443                        else {
2444                            for (int row = rowCount - 1; row >= 0; row--) {
2445                                renderer.drawItem(
2446                                    g2, state, dataArea, this, domainAxis,
2447                                    rangeAxis, currentDataset, row, column, pass
2448                                );
2449                            }
2450                        }
2451                    }
2452                }
2453                else {
2454                    for (int column = columnCount - 1; column >= 0; column--) {
2455                        if (this.rowRenderingOrder == SortOrder.ASCENDING) {
2456                            for (int row = 0; row < rowCount; row++) {
2457                                renderer.drawItem(
2458                                    g2, state, dataArea, this, domainAxis,
2459                                    rangeAxis, currentDataset, row, column, pass
2460                                );
2461                            }
2462                        }
2463                        else {
2464                            for (int row = rowCount - 1; row >= 0; row--) {
2465                                renderer.drawItem(
2466                                    g2, state, dataArea, this, domainAxis,
2467                                    rangeAxis, currentDataset, row, column, pass
2468                                );
2469                            }
2470                        }
2471                    }
2472                }
2473            }
2474        }
2475        return foundData;
2476        
2477    }
2478
2479    /**
2480     * Draws the gridlines for the plot.
2481     *
2482     * @param g2 the graphics device.
2483     * @param dataArea the area inside the axes.
2484     */

2485    protected void drawDomainGridlines(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea) {
2486
2487        // draw the domain grid lines, if any...
2488
if (isDomainGridlinesVisible()) {
2489            CategoryAnchor anchor = getDomainGridlinePosition();
2490            RectangleEdge domainAxisEdge = getDomainAxisEdge();
2491            Stroke JavaDoc gridStroke = getDomainGridlineStroke();
2492            Paint JavaDoc gridPaint = getDomainGridlinePaint();
2493            if ((gridStroke != null) && (gridPaint != null)) {
2494                // iterate over the categories
2495
CategoryDataset data = getDataset();
2496                if (data != null) {
2497                    CategoryAxis axis = getDomainAxis();
2498                    if (axis != null) {
2499                        int columnCount = data.getColumnCount();
2500                        for (int c = 0; c < columnCount; c++) {
2501                            double xx = axis.getCategoryJava2DCoordinate(
2502                                anchor, c, columnCount, dataArea, domainAxisEdge
2503                            );
2504                            CategoryItemRenderer renderer1 = getRenderer();
2505                            if (renderer1 != null) {
2506                                renderer1.drawDomainGridline(
2507                                    g2, this, dataArea, xx
2508                                );
2509                            }
2510                        }
2511                    }
2512                }
2513            }
2514        }
2515    }
2516 
2517    /**
2518     * Draws the gridlines for the plot.
2519     *
2520     * @param g2 the graphics device.
2521     * @param dataArea the area inside the axes.
2522     * @param ticks the ticks.
2523     */

2524    protected void drawRangeGridlines(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2525                                      List JavaDoc ticks) {
2526        // draw the range grid lines, if any...
2527
if (isRangeGridlinesVisible()) {
2528            Stroke JavaDoc gridStroke = getRangeGridlineStroke();
2529            Paint JavaDoc gridPaint = getRangeGridlinePaint();
2530            if ((gridStroke != null) && (gridPaint != null)) {
2531                ValueAxis axis = getRangeAxis();
2532                if (axis != null) {
2533                    Iterator JavaDoc iterator = ticks.iterator();
2534                    while (iterator.hasNext()) {
2535                        ValueTick tick = (ValueTick) iterator.next();
2536                        CategoryItemRenderer renderer1 = getRenderer();
2537                        if (renderer1 != null) {
2538                            renderer1.drawRangeGridline(
2539                                g2, this, getRangeAxis(), dataArea,
2540                                tick.getValue()
2541                            );
2542                        }
2543                    }
2544                }
2545            }
2546        }
2547    }
2548
2549    /**
2550     * Draws the annotations...
2551     *
2552     * @param g2 the graphics device.
2553     * @param dataArea the data area.
2554     */

2555    protected void drawAnnotations(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea) {
2556
2557        if (getAnnotations() != null) {
2558            Iterator JavaDoc iterator = getAnnotations().iterator();
2559            while (iterator.hasNext()) {
2560                CategoryAnnotation annotation
2561                    = (CategoryAnnotation) iterator.next();
2562                annotation.draw(
2563                    g2, this, dataArea, getDomainAxis(), getRangeAxis()
2564                );
2565            }
2566        }
2567
2568    }
2569
2570    /**
2571     * Draws the domain markers (if any) for an axis and layer. This method is
2572     * typically called from within the draw() method.
2573     *
2574     * @param g2 the graphics device.
2575     * @param dataArea the data area.
2576     * @param index the renderer index.
2577     * @param layer the layer (foreground or background).
2578     */

2579    protected void drawDomainMarkers(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2580                                     int index, Layer layer) {
2581                                                 
2582        CategoryItemRenderer r = getRenderer(index);
2583        if (r == null) {
2584            return;
2585        }
2586        
2587        Collection JavaDoc markers = getDomainMarkers(index, layer);
2588        CategoryAxis axis = getDomainAxisForDataset(index);
2589        if (markers != null && axis != null) {
2590            Iterator JavaDoc iterator = markers.iterator();
2591            while (iterator.hasNext()) {
2592                CategoryMarker marker = (CategoryMarker) iterator.next();
2593                r.drawDomainMarker(g2, this, axis, marker, dataArea);
2594            }
2595        }
2596        
2597    }
2598
2599    /**
2600     * Draws the range markers (if any) for an axis and layer. This method is
2601     * typically called from within the draw() method.
2602     *
2603     * @param g2 the graphics device.
2604     * @param dataArea the data area.
2605     * @param index the renderer index.
2606     * @param layer the layer (foreground or background).
2607     */

2608    protected void drawRangeMarkers(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
2609                                    int index, Layer layer) {
2610                                                 
2611        CategoryItemRenderer r = getRenderer(index);
2612        if (r == null) {
2613            return;
2614        }
2615        
2616        Collection JavaDoc markers = getRangeMarkers(index, layer);
2617        ValueAxis axis = getRangeAxisForDataset(index);
2618        if (markers != null && axis != null) {
2619            Iterator JavaDoc iterator = markers.iterator();
2620            while (iterator.hasNext()) {
2621                Marker marker = (Marker) iterator.next();
2622                r.drawRangeMarker(g2, this, axis, marker, dataArea);
2623            }
2624        }
2625        
2626    }
2627
2628    /**
2629     * Utility method for drawing a line perpendicular to the range axis (used
2630     * for crosshairs).
2631     *
2632     * @param g2 the graphics device.
2633     * @param dataArea the area defined by the axes.
2634     * @param value the data value.
2635     * @param stroke the line stroke.
2636     * @param paint the line paint.
2637     */

2638    protected void drawRangeLine(Graphics2D JavaDoc g2,
2639                                 Rectangle2D JavaDoc dataArea,
2640                                 double value, Stroke JavaDoc stroke, Paint JavaDoc paint) {
2641
2642        double java2D = getRangeAxis().valueToJava2D(
2643            value, dataArea, getRangeAxisEdge()
2644        );
2645        Line2D JavaDoc line = null;
2646        if (this.orientation == PlotOrientation.HORIZONTAL) {
2647            line = new Line2D.Double JavaDoc(
2648                java2D, dataArea.getMinY(), java2D, dataArea.getMaxY()
2649            );
2650        }
2651        else if (this.orientation == PlotOrientation.VERTICAL) {
2652            line = new Line2D.Double JavaDoc(
2653                dataArea.getMinX(), java2D, dataArea.getMaxX(), java2D
2654            );
2655        }
2656        g2.setStroke(stroke);
2657        g2.setPaint(paint);
2658        g2.draw(line);
2659
2660    }
2661
2662    /**
2663     * Returns the range of data values that will be plotted against the range
2664     * axis. If the dataset is <code>null</code>, this method returns
2665     * <code>null</code>.
2666     *
2667     * @param axis the axis.
2668     *
2669     * @return The data range.
2670     */

2671    public Range getDataRange(ValueAxis axis) {
2672
2673        Range result = null;
2674        List JavaDoc mappedDatasets = new ArrayList JavaDoc();
2675        
2676        int rangeIndex = this.rangeAxes.indexOf(axis);
2677        if (rangeIndex >= 0) {
2678            mappedDatasets.addAll(datasetsMappedToRangeAxis(rangeIndex));
2679        }
2680        else if (axis == getRangeAxis()) {
2681            mappedDatasets.addAll(datasetsMappedToRangeAxis(0));
2682        }
2683
2684        // iterate through the datasets that map to the axis and get the union
2685
// of the ranges.
2686
Iterator JavaDoc iterator = mappedDatasets.iterator();
2687        while (iterator.hasNext()) {
2688            CategoryDataset d = (CategoryDataset) iterator.next();
2689            CategoryItemRenderer r = getRendererForDataset(d);
2690            if (r != null) {
2691                result = Range.combine(result, r.findRangeBounds(d));
2692            }
2693        }
2694        return result;
2695
2696    }
2697
2698    /**
2699     * A utility method that returns a list of datasets that are mapped to a
2700     * given range axis.
2701     *
2702     * @param index the axis index.
2703     *
2704     * @return A list of datasets.
2705     */

2706    private List JavaDoc datasetsMappedToRangeAxis(int index) {
2707        List JavaDoc result = new ArrayList JavaDoc();
2708        for (int i = 0; i < this.datasets.size(); i++) {
2709            Object JavaDoc dataset = this.datasets.get(i);
2710            if (dataset != null) {
2711                Integer JavaDoc m = (Integer JavaDoc) this.datasetToRangeAxisMap.get(i);
2712                if (m == null) { // a dataset with no mapping is assigned to
2713
// axis 0
2714
if (index == 0) {
2715                        result.add(dataset);
2716                    }
2717                }
2718                else {
2719                    if (m.intValue() == index) {
2720                        result.add(dataset);
2721                    }
2722                }
2723            }
2724        }
2725        return result;
2726    }
2727
2728    /**
2729     * Returns the weight for this plot when it is used as a subplot within a
2730     * combined plot.
2731     *
2732     * @return The weight.
2733     */

2734    public int getWeight() {
2735        return this.weight;
2736    }
2737
2738    /**
2739     * Sets the weight for the plot.
2740     *
2741     * @param weight the weight.
2742     */

2743    public void setWeight(int weight) {
2744        this.weight = weight;
2745    }
2746    
2747    /**
2748     * Returns the fixed domain axis space.
2749     *
2750     * @return The fixed domain axis space (possibly <code>null</code>).
2751     */

2752    public AxisSpace getFixedDomainAxisSpace() {
2753        return this.fixedDomainAxisSpace;
2754    }
2755
2756    /**
2757     * Sets the fixed domain axis space.
2758     *
2759     * @param space the space (<code>null</code> permitted).
2760     */

2761    public void setFixedDomainAxisSpace(AxisSpace space) {
2762        this.fixedDomainAxisSpace = space;
2763    }
2764
2765    /**
2766     * Returns the fixed range axis space.
2767     *
2768     * @return The fixed range axis space (possibly <code>null</code>).
2769     */

2770    public AxisSpace getFixedRangeAxisSpace() {
2771        return this.fixedRangeAxisSpace;
2772    }
2773
2774    /**
2775     * Sets the fixed range axis space.
2776     *
2777     * @param space the space (<code>null</code> permitted).
2778     */

2779    public void setFixedRangeAxisSpace(AxisSpace space) {
2780        this.fixedRangeAxisSpace = space;
2781    }
2782
2783    /**
2784     * Returns a list of the categories for the plot.
2785     *
2786     * @return A list of the categories for the plot.
2787     */

2788    public List JavaDoc getCategories() {
2789        List JavaDoc result = null;
2790        if (getDataset() != null) {
2791            result = Collections.unmodifiableList(getDataset().getColumnKeys());
2792        }
2793        return result;
2794    }
2795
2796    /**
2797     * Returns the flag that controls whether or not the shared domain axis is
2798     * drawn for each subplot.
2799     *
2800     * @return A boolean.
2801     */

2802    public boolean getDrawSharedDomainAxis() {
2803        return this.drawSharedDomainAxis;
2804    }
2805    
2806    /**
2807     * Sets the flag that controls whether the shared domain axis is drawn when
2808     * this plot is being used as a subplot.
2809     *
2810     * @param draw a boolean.
2811     */

2812    public void setDrawSharedDomainAxis(boolean draw) {
2813        this.drawSharedDomainAxis = draw;
2814        notifyListeners(new PlotChangeEvent(this));
2815    }
2816
2817    /**
2818     * Returns <code>false</code>.
2819     *
2820     * @return A boolean.
2821     */

2822    public boolean isDomainZoomable() {
2823        return false;
2824    }
2825    
2826    /**
2827     * Returns <code>false</code>.
2828     *
2829     * @return A boolean.
2830     */

2831    public boolean isRangeZoomable() {
2832        return true;
2833    }
2834
2835    /**
2836     * This method does nothing, because <code>CategoryPlot</code> doesn't
2837     * support zooming on the domain.
2838     *
2839     * @param factor the zoom factor.
2840     * @param state the plot state.
2841     * @param source the source point (in Java2D space) for the zoom.
2842     */

2843    public void zoomDomainAxes(double factor, PlotRenderingInfo state,
2844                               Point2D JavaDoc source) {
2845        // can't zoom domain axis
2846
}
2847
2848    /**
2849     * This method does nothing, because <code>CategoryPlot</code> doesn't
2850     * support zooming on the domain.
2851     *
2852     * @param lowerPercent the lower bound.
2853     * @param upperPercent the upper bound.
2854     * @param state the plot state.
2855     * @param source the source point (in Java2D space) for the zoom.
2856     */

2857    public void zoomDomainAxes(double lowerPercent, double upperPercent,
2858                               PlotRenderingInfo state, Point2D JavaDoc source) {
2859        // can't zoom domain axis
2860
}
2861
2862    /**
2863     * Multiplies the range on the range axis/axes by the specified factor.
2864     *
2865     * @param factor the zoom factor.
2866     * @param state the plot state.
2867     * @param source the source point (in Java2D space) for the zoom.
2868     */

2869    public void zoomRangeAxes(double factor, PlotRenderingInfo state,
2870                              Point2D JavaDoc source) {
2871        for (int i = 0; i < this.rangeAxes.size(); i++) {
2872            ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
2873            if (rangeAxis != null) {
2874                rangeAxis.resizeRange(factor);
2875            }
2876        }
2877    }
2878
2879    /**
2880     * Zooms in on the range axes.
2881     *
2882     * @param lowerPercent the lower bound.
2883     * @param upperPercent the upper bound.
2884     * @param state the plot state.
2885     * @param source the source point (in Java2D space) for the zoom.
2886     */

2887    public void zoomRangeAxes(double lowerPercent, double upperPercent,
2888                              PlotRenderingInfo state, Point2D JavaDoc source) {
2889        for (int i = 0; i < this.rangeAxes.size(); i++) {
2890            ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i);
2891            if (rangeAxis != null) {
2892                rangeAxis.zoomRange(lowerPercent, upperPercent);
2893            }
2894        }
2895    }
2896    
2897    /**
2898     * Returns the anchor value.
2899     *
2900     * @return The anchor value.
2901     */

2902    public double getAnchorValue() {
2903        return this.anchorValue;
2904    }
2905
2906    /**
2907     * Sets the anchor value.
2908     *
2909     * @param value the anchor value.
2910     */

2911    public void setAnchorValue(double value) {
2912        setAnchorValue(value, true);
2913    }
2914
2915    /**
2916     * Sets the anchor value.
2917     *
2918     * @param value the value.
2919     * @param notify notify listeners?
2920     */

2921    public void setAnchorValue(double value, boolean notify) {
2922        this.anchorValue = value;
2923        if (notify) {
2924            notifyListeners(new PlotChangeEvent(this));
2925        }
2926    }
2927    
2928    /**
2929     * Tests the plot for equality with an arbitrary object.
2930     *
2931     * @param obj the object to test against (<code>null</code> permitted).
2932     *
2933     * @return A boolean.
2934     */

2935    public boolean equals(Object JavaDoc obj) {
2936    
2937        if (obj == this) {
2938            return true;
2939        }
2940        
2941        if (!(obj instanceof CategoryPlot)) {
2942            return false;
2943        }
2944        if (!super.equals(obj)) {
2945            return false;
2946        }
2947
2948        CategoryPlot that = (CategoryPlot) obj;
2949            
2950        if (this.orientation != that.orientation) {
2951            return false;
2952        }
2953        if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) {
2954            return false;
2955        }
2956        if (!this.domainAxes.equals(that.domainAxes)) {
2957            return false;
2958        }
2959        if (!this.domainAxisLocations.equals(that.domainAxisLocations)) {
2960            return false;
2961        }
2962        if (this.drawSharedDomainAxis != that.drawSharedDomainAxis) {
2963            return false;
2964        }
2965        if (!this.rangeAxes.equals(that.rangeAxes)) {
2966            return false;
2967        }
2968        if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) {
2969            return false;
2970        }
2971        if (!ObjectUtilities.equal(
2972            this.datasetToDomainAxisMap, that.datasetToDomainAxisMap
2973        )) {
2974            return false;
2975        }
2976        if (!ObjectUtilities.equal(
2977            this.datasetToRangeAxisMap, that.datasetToRangeAxisMap
2978        )) {
2979            return false;
2980        }
2981        if (!ObjectUtilities.equal(this.renderers, that.renderers)) {
2982            return false;
2983        }
2984        if (this.renderingOrder != that.renderingOrder) {
2985            return false;
2986        }
2987        if (this.columnRenderingOrder != that.columnRenderingOrder) {
2988            return false;
2989        }
2990        if (this.rowRenderingOrder != that.rowRenderingOrder) {
2991            return false;
2992        }
2993        if (this.domainGridlinesVisible != that.domainGridlinesVisible) {
2994            return false;
2995        }
2996        if (this.domainGridlinePosition != that.domainGridlinePosition) {
2997            return false;
2998        }
2999        if (!ObjectUtilities.equal(
3000            this.domainGridlineStroke, that.domainGridlineStroke
3001        )) {
3002            return false;
3003        }
3004        if (!ObjectUtilities.equal(
3005            this.domainGridlinePaint, that.domainGridlinePaint
3006        )) {
3007            return false;
3008        }
3009        if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) {
3010            return false;
3011        }
3012        if (!ObjectUtilities.equal(
3013            this.rangeGridlineStroke, that.rangeGridlineStroke
3014        )) {
3015            return false;
3016        }
3017        if (!ObjectUtilities.equal(
3018            this.rangeGridlinePaint, that.rangeGridlinePaint
3019        )) {
3020            return false;
3021        }
3022        if (this.anchorValue != that.anchorValue) {
3023            return false;
3024        }
3025        if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) {
3026            return false;
3027        }
3028        if (this.rangeCrosshairValue != that.rangeCrosshairValue) {
3029            return false;
3030        }
3031        if (!ObjectUtilities.equal(
3032            this.rangeCrosshairStroke, that.rangeCrosshairStroke
3033        )) {
3034            return false;
3035        }
3036        if (!ObjectUtilities.equal(
3037            this.rangeCrosshairPaint, that.rangeCrosshairPaint
3038        )) {
3039            return false;
3040        }
3041        if (
3042            this.rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData
3043        ) {
3044            return false;
3045        }
3046        if (!ObjectUtilities.equal(
3047            this.foregroundRangeMarkers, that.foregroundRangeMarkers
3048        )) {
3049            return false;
3050        }
3051        if (!ObjectUtilities.equal(
3052            this.backgroundRangeMarkers, that.backgroundRangeMarkers
3053        )) {
3054            return false;
3055        }
3056        if (!ObjectUtilities.equal(this.annotations, that.annotations)) {
3057            return false;
3058        }
3059        if (this.weight != that.weight) {
3060            return false;
3061        }
3062        if (!ObjectUtilities.equal(
3063            this.fixedDomainAxisSpace, that.fixedDomainAxisSpace
3064        )) {
3065            return false;
3066        }
3067        if (!ObjectUtilities.equal(
3068            this.fixedRangeAxisSpace, that.fixedRangeAxisSpace
3069        )) {
3070            return false;
3071        }
3072        
3073        return true;
3074        
3075    }
3076    
3077    /**
3078     * Returns a clone of the plot.
3079     *
3080     * @return A clone.
3081     *
3082     * @throws CloneNotSupportedException if the cloning is not supported.
3083     */

3084    public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
3085        
3086        CategoryPlot clone = (CategoryPlot) super.clone();
3087        
3088        clone.domainAxes = new ObjectList();
3089        for (int i = 0; i < this.domainAxes.size(); i++) {
3090            CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i);
3091            if (xAxis != null) {
3092                CategoryAxis clonedAxis = (CategoryAxis) xAxis.clone();
3093                clone.setDomainAxis(i, clonedAxis);
3094            }
3095        }
3096        clone.domainAxisLocations
3097            = (ObjectList) this.domainAxisLocations.clone();
3098
3099        clone.rangeAxes = new ObjectList();
3100        for (int i = 0; i < this.rangeAxes.size(); i++) {
3101            ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i);
3102            if (yAxis != null) {
3103                ValueAxis clonedAxis = (ValueAxis) yAxis.clone();
3104                clone.setRangeAxis(i, clonedAxis);
3105            }
3106        }
3107        clone.rangeAxisLocations = (ObjectList) this.rangeAxisLocations.clone();
3108
3109        clone.datasets = (ObjectList) this.datasets.clone();
3110        for (int i = 0; i < clone.datasets.size(); i++) {
3111            CategoryDataset dataset = clone.getDataset(i);
3112            if (dataset != null) {
3113                dataset.addChangeListener(clone);
3114            }
3115        }
3116        clone.datasetToDomainAxisMap
3117            = (ObjectList) this.datasetToDomainAxisMap.clone();
3118        clone.datasetToRangeAxisMap
3119            = (ObjectList) this.datasetToRangeAxisMap.clone();
3120        clone.renderers = (ObjectList) this.renderers.clone();
3121        if (this.fixedDomainAxisSpace != null) {
3122            clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone(
3123                this.fixedDomainAxisSpace
3124            );
3125        }
3126        if (this.fixedRangeAxisSpace != null) {
3127            clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone(
3128                this.fixedRangeAxisSpace
3129            );
3130        }
3131        
3132        return clone;
3133            
3134    }
3135    
3136    /**
3137     * Provides serialization support.
3138     *
3139     * @param stream the output stream.
3140     *
3141     * @throws IOException if there is an I/O error.
3142     */

3143    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
3144        stream.defaultWriteObject();
3145        SerialUtilities.writeStroke(this.domainGridlineStroke, stream);
3146        SerialUtilities.writePaint(this.domainGridlinePaint, stream);
3147        SerialUtilities.writeStroke(this.rangeGridlineStroke, stream);
3148        SerialUtilities.writePaint(this.rangeGridlinePaint, stream);
3149        SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream);
3150        SerialUtilities.writePaint(this.rangeCrosshairPaint, stream);
3151    }
3152
3153    /**
3154     * Provides serialization support.
3155     *
3156     * @param stream the input stream.
3157     *
3158     * @throws IOException if there is an I/O error.
3159     * @throws ClassNotFoundException if there is a classpath problem.
3160     */

3161    private void readObject(ObjectInputStream JavaDoc stream)
3162        throws IOException JavaDoc, ClassNotFoundException JavaDoc {
3163
3164        stream.defaultReadObject();
3165        this.domainGridlineStroke = SerialUtilities.readStroke(stream);
3166        this.domainGridlinePaint = SerialUtilities.readPaint(stream);
3167        this.rangeGridlineStroke = SerialUtilities.readStroke(stream);
3168        this.rangeGridlinePaint = SerialUtilities.readPaint(stream);
3169        this.rangeCrosshairStroke = SerialUtilities.readStroke(stream);
3170        this.rangeCrosshairPaint = SerialUtilities.readPaint(stream);
3171
3172        for (int i = 0; i < this.domainAxes.size(); i++) {
3173            CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i);
3174            if (xAxis != null) {
3175                xAxis.setPlot(this);
3176                xAxis.addChangeListener(this);
3177            }
3178        }
3179        for (int i = 0; i < this.rangeAxes.size(); i++) {
3180            ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i);
3181            if (yAxis != null) {
3182                yAxis.setPlot(this);
3183                yAxis.addChangeListener(this);
3184            }
3185        }
3186        int datasetCount = this.datasets.size();
3187        for (int i = 0; i < datasetCount; i++) {
3188            Dataset dataset = (Dataset) this.datasets.get(i);
3189            if (dataset != null) {
3190                dataset.addChangeListener(this);
3191            }
3192        }
3193        int rendererCount = this.renderers.size();
3194        for (int i = 0; i < rendererCount; i++) {
3195            CategoryItemRenderer renderer
3196                = (CategoryItemRenderer) this.renderers.get(i);
3197            if (renderer != null) {
3198                renderer.addChangeListener(this);
3199            }
3200        }
3201
3202    }
3203
3204}
3205
Popular Tags