KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ------------------
28  * SpiderWebPlot.java
29  * ------------------
30  * (C) Copyright 2005, 2006, by Heaps of Flavour Pty Ltd and Contributors.
31  *
32  * Company Info: http://www.i4-talent.com
33  *
34  * Original Author: Don Elliott;
35  * Contributor(s): David Gilbert (for Object Refinery Limited);
36  * Nina Jeliazkova;
37  *
38  * $Id: SpiderWebPlot.java,v 1.11.2.10 2006/08/01 16:29:32 mungady Exp $
39  *
40  * Changes (from 28-Jan-2005)
41  * --------------------------
42  * 28-Jan-2005 : First cut - missing a few features - still to do:
43  * - needs tooltips/URL/label generator functions
44  * - ticks on axes / background grid?
45  * 31-Jan-2005 : Renamed SpiderWebPlot, added label generator support, and
46  * reformatted for consistency with other source files in
47  * JFreeChart (DG);
48  * 20-Apr-2005 : Renamed CategoryLabelGenerator
49  * --> CategoryItemLabelGenerator (DG);
50  * 05-May-2005 : Updated draw() method parameters (DG);
51  * 10-Jun-2005 : Added equals() method and fixed serialization (DG);
52  * 16-Jun-2005 : Added default constructor and get/setDataset()
53  * methods (DG);
54  * ------------- JFREECHART 1.0.0 ---------------------------------------------
55  * 05-Apr-2006 : Fixed bug preventing the display of zero values - see patch
56  * 1462727 (DG);
57  * 05-Apr-2006 : Added support for mouse clicks, tool tips and URLs - see patch
58  * 1463455 (DG);
59  * 01-Jun-2006 : Fix bug 1493199, NullPointerException when drawing with null
60  * info (DG);
61  *
62  */

63
64 package org.jfree.chart.plot;
65
66 import java.awt.AlphaComposite JavaDoc;
67 import java.awt.BasicStroke JavaDoc;
68 import java.awt.Color JavaDoc;
69 import java.awt.Composite JavaDoc;
70 import java.awt.Font JavaDoc;
71 import java.awt.Graphics2D JavaDoc;
72 import java.awt.Paint JavaDoc;
73 import java.awt.Polygon JavaDoc;
74 import java.awt.Rectangle JavaDoc;
75 import java.awt.Shape JavaDoc;
76 import java.awt.Stroke JavaDoc;
77 import java.awt.font.FontRenderContext JavaDoc;
78 import java.awt.font.LineMetrics JavaDoc;
79 import java.awt.geom.Arc2D JavaDoc;
80 import java.awt.geom.Ellipse2D JavaDoc;
81 import java.awt.geom.Line2D JavaDoc;
82 import java.awt.geom.Point2D JavaDoc;
83 import java.awt.geom.Rectangle2D JavaDoc;
84 import java.io.IOException JavaDoc;
85 import java.io.ObjectInputStream JavaDoc;
86 import java.io.ObjectOutputStream JavaDoc;
87 import java.io.Serializable JavaDoc;
88 import java.util.Iterator JavaDoc;
89 import java.util.List JavaDoc;
90
91 import org.jfree.chart.LegendItem;
92 import org.jfree.chart.LegendItemCollection;
93 import org.jfree.chart.entity.CategoryItemEntity;
94 import org.jfree.chart.entity.EntityCollection;
95 import org.jfree.chart.event.PlotChangeEvent;
96 import org.jfree.chart.labels.CategoryItemLabelGenerator;
97 import org.jfree.chart.labels.CategoryToolTipGenerator;
98 import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
99 import org.jfree.chart.urls.CategoryURLGenerator;
100 import org.jfree.data.category.CategoryDataset;
101 import org.jfree.data.general.DatasetChangeEvent;
102 import org.jfree.data.general.DatasetUtilities;
103 import org.jfree.io.SerialUtilities;
104 import org.jfree.ui.RectangleInsets;
105 import org.jfree.util.ObjectUtilities;
106 import org.jfree.util.PaintList;
107 import org.jfree.util.PaintUtilities;
108 import org.jfree.util.Rotation;
109 import org.jfree.util.ShapeUtilities;
110 import org.jfree.util.StrokeList;
111 import org.jfree.util.TableOrder;
112
113 /**
114  * A plot that displays data from a {@link CategoryDataset} in the form of a
115  * "spider web". Multiple series can be plotted on the same axis to allow
116  * easy comparison. This plot doesn't support negative values at present.
117  */

118 public class SpiderWebPlot extends Plot implements Cloneable JavaDoc, Serializable JavaDoc {
119     
120     /** For serialization. */
121     private static final long serialVersionUID = -5376340422031599463L;
122     
123     /** The default head radius percent (currently 1%). */
124     public static final double DEFAULT_HEAD = 0.01;
125
126     /** The default axis label gap (currently 10%). */
127     public static final double DEFAULT_AXIS_LABEL_GAP = 0.10;
128  
129     /** The default interior gap. */
130     public static final double DEFAULT_INTERIOR_GAP = 0.25;
131
132     /** The maximum interior gap (currently 40%). */
133     public static final double MAX_INTERIOR_GAP = 0.40;
134
135     /** The default starting angle for the radar chart axes. */
136     public static final double DEFAULT_START_ANGLE = 90.0;
137
138     /** The default series label font. */
139     public static final Font JavaDoc DEFAULT_LABEL_FONT = new Font JavaDoc("SansSerif",
140             Font.PLAIN, 10);
141     
142     /** The default series label paint. */
143     public static final Paint JavaDoc DEFAULT_LABEL_PAINT = Color.black;
144
145     /** The default series label background paint. */
146     public static final Paint JavaDoc DEFAULT_LABEL_BACKGROUND_PAINT
147         = new Color JavaDoc(255, 255, 192);
148
149     /** The default series label outline paint. */
150     public static final Paint JavaDoc DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
151
152     /** The default series label outline stroke. */
153     public static final Stroke JavaDoc DEFAULT_LABEL_OUTLINE_STROKE
154         = new BasicStroke JavaDoc(0.5f);
155
156     /** The default series label shadow paint. */
157     public static final Paint JavaDoc DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray;
158
159     /**
160      * The default maximum value plotted - forces the plot to evaluate
161      * the maximum from the data passed in
162      */

163     public static final double DEFAULT_MAX_VALUE = -1.0;
164
165     /** The head radius as a percentage of the available drawing area. */
166     protected double headPercent;
167
168     /** The space left around the outside of the plot as a percentage. */
169     private double interiorGap;
170
171     /** The gap between the labels and the axes as a %age of the radius. */
172     private double axisLabelGap;
173
174     /** The dataset. */
175     private CategoryDataset dataset;
176
177     /** The maximum value we are plotting against on each category axis */
178     private double maxValue;
179   
180     /**
181      * The data extract order (BY_ROW or BY_COLUMN). This denotes whether
182      * the data series are stored in rows (in which case the category names are
183      * derived from the column keys) or in columns (in which case the category
184      * names are derived from the row keys).
185      */

186     private TableOrder dataExtractOrder;
187
188     /** The starting angle. */
189     private double startAngle;
190
191     /** The direction for drawing the radar axis & plots. */
192     private Rotation direction;
193
194     /** The legend item shape. */
195     private transient Shape JavaDoc legendItemShape;
196
197     /** The paint for ALL series (overrides list). */
198     private transient Paint JavaDoc seriesPaint;
199
200     /** The series paint list. */
201     private PaintList seriesPaintList;
202
203     /** The base series paint (fallback). */
204     private transient Paint JavaDoc baseSeriesPaint;
205
206     /** The outline paint for ALL series (overrides list). */
207     private transient Paint JavaDoc seriesOutlinePaint;
208
209     /** The series outline paint list. */
210     private PaintList seriesOutlinePaintList;
211
212     /** The base series outline paint (fallback). */
213     private transient Paint JavaDoc baseSeriesOutlinePaint;
214
215     /** The outline stroke for ALL series (overrides list). */
216     private transient Stroke JavaDoc seriesOutlineStroke;
217
218     /** The series outline stroke list. */
219     private StrokeList seriesOutlineStrokeList;
220
221     /** The base series outline stroke (fallback). */
222     private transient Stroke JavaDoc baseSeriesOutlineStroke;
223
224     /** The font used to display the category labels. */
225     private Font JavaDoc labelFont;
226
227     /** The color used to draw the category labels. */
228     private transient Paint JavaDoc labelPaint;
229     
230     /** The label generator. */
231     private CategoryItemLabelGenerator labelGenerator;
232
233     /** controls if the web polygons are filled or not */
234     private boolean webFilled = true;
235     
236     /** A tooltip generator for the plot (<code>null</code> permitted). */
237     private CategoryToolTipGenerator toolTipGenerator;
238     
239     /** A URL generator for the plot (<code>null</code> permitted). */
240     private CategoryURLGenerator urlGenerator;
241   
242     /**
243      * Creates a default plot with no dataset.
244      */

245     public SpiderWebPlot() {
246         this(null);
247     }
248     
249     /**
250      * Creates a new spider web plot with the given dataset, with each row
251      * representing a series.
252      *
253      * @param dataset the dataset (<code>null</code> permitted).
254      */

255     public SpiderWebPlot(CategoryDataset dataset) {
256         this(dataset, TableOrder.BY_ROW);
257     }
258
259     /**
260      * Creates a new spider web plot with the given dataset.
261      *
262      * @param dataset the dataset.
263      * @param extract controls how data is extracted ({@link TableOrder#BY_ROW}
264      * or {@link TableOrder#BY_COLUMN}).
265      */

266     public SpiderWebPlot(CategoryDataset dataset, TableOrder extract) {
267         super();
268         if (extract == null) {
269             throw new IllegalArgumentException JavaDoc("Null 'extract' argument.");
270         }
271         this.dataset = dataset;
272         if (dataset != null) {
273             dataset.addChangeListener(this);
274         }
275
276         this.dataExtractOrder = extract;
277         this.headPercent = DEFAULT_HEAD;
278         this.axisLabelGap = DEFAULT_AXIS_LABEL_GAP;
279
280         this.interiorGap = DEFAULT_INTERIOR_GAP;
281         this.startAngle = DEFAULT_START_ANGLE;
282         this.direction = Rotation.CLOCKWISE;
283         this.maxValue = DEFAULT_MAX_VALUE;
284
285         this.seriesPaint = null;
286         this.seriesPaintList = new PaintList();
287         this.baseSeriesPaint = null;
288
289         this.seriesOutlinePaint = null;
290         this.seriesOutlinePaintList = new PaintList();
291         this.baseSeriesOutlinePaint = DEFAULT_OUTLINE_PAINT;
292
293         this.seriesOutlineStroke = null;
294         this.seriesOutlineStrokeList = new StrokeList();
295         this.baseSeriesOutlineStroke = DEFAULT_OUTLINE_STROKE;
296
297         this.labelFont = DEFAULT_LABEL_FONT;
298         this.labelPaint = DEFAULT_LABEL_PAINT;
299         this.labelGenerator = new StandardCategoryItemLabelGenerator();
300         
301         this.legendItemShape = DEFAULT_LEGEND_ITEM_CIRCLE;
302     }
303
304     /**
305      * Returns a short string describing the type of plot.
306      *
307      * @return The plot type.
308      */

309     public String JavaDoc getPlotType() {
310         // return localizationResources.getString("Radar_Plot");
311
return ("Spider Web Plot");
312     }
313     
314     /**
315      * Returns the dataset.
316      *
317      * @return The dataset (possibly <code>null</code>).
318      */

319     public CategoryDataset getDataset() {
320         return this.dataset;
321     }
322     
323     /**
324      * Sets the dataset used by the plot and sends a {@link PlotChangeEvent}
325      * to all registered listeners.
326      *
327      * @param dataset the dataset (<code>null</code> permitted).
328      */

329     public void setDataset(CategoryDataset dataset) {
330         // if there is an existing dataset, remove the plot from the list of
331
// change listeners...
332
if (this.dataset != null) {
333             this.dataset.removeChangeListener(this);
334         }
335
336         // set the new dataset, and register the chart as a change listener...
337
this.dataset = dataset;
338         if (dataset != null) {
339             setDatasetGroup(dataset.getGroup());
340             dataset.addChangeListener(this);
341         }
342
343         // send a dataset change event to self to trigger plot change event
344
datasetChanged(new DatasetChangeEvent(this, dataset));
345     }
346     
347     /**
348      * Method to determine if the web chart is to be filled.
349      *
350      * @return A boolean.
351      */

352     public boolean isWebFilled() {
353         return this.webFilled;
354     }
355
356     /**
357      * Sets the webFilled flag and sends a {@link PlotChangeEvent} to all
358      * registered listeners.
359      *
360      * @param flag the flag.
361      */

362     public void setWebFilled(boolean flag) {
363         this.webFilled = flag;
364         notifyListeners(new PlotChangeEvent(this));
365     }
366   
367     /**
368      * Returns the data extract order (by row or by column).
369      *
370      * @return The data extract order (never <code>null</code>).
371      *
372      * @see #setDataExtractOrder(TableOrder)
373      */

374     public TableOrder getDataExtractOrder() {
375         return this.dataExtractOrder;
376     }
377
378     /**
379      * Sets the data extract order (by row or by column) and sends a
380      * {@link PlotChangeEvent}to all registered listeners.
381      *
382      * @param order the order (<code>null</code> not permitted).
383      *
384      * @throws IllegalArgumentException if <code>order</code> is
385      * <code>null</code>.
386      *
387      * @see #getDataExtractOrder()
388      */

389     public void setDataExtractOrder(TableOrder order) {
390         if (order == null) {
391             throw new IllegalArgumentException JavaDoc("Null 'order' argument");
392         }
393         this.dataExtractOrder = order;
394         notifyListeners(new PlotChangeEvent(this));
395     }
396
397     /**
398      * Returns the head percent.
399      *
400      * @return The head percent.
401      */

402     public double getHeadPercent() {
403         return this.headPercent;
404     }
405     
406     /**
407      * Sets the head percent and sends a {@link PlotChangeEvent} to all
408      * registered listeners.
409      *
410      * @param percent the percent.
411      */

412     public void setHeadPercent(double percent) {
413         this.headPercent = percent;
414         notifyListeners(new PlotChangeEvent(this));
415     }
416     
417     /**
418      * Returns the start angle for the first radar axis.
419      * <BR>
420      * This is measured in degrees starting from 3 o'clock (Java Arc2D default)
421      * and measuring anti-clockwise.
422      *
423      * @return The start angle.
424      */

425     public double getStartAngle() {
426         return this.startAngle;
427     }
428
429     /**
430      * Sets the starting angle and sends a {@link PlotChangeEvent} to all
431      * registered listeners.
432      * <P>
433      * The initial default value is 90 degrees, which corresponds to 12 o'clock.
434      * A value of zero corresponds to 3 o'clock... this is the encoding used by
435      * Java's Arc2D class.
436      *
437      * @param angle the angle (in degrees).
438      */

439     public void setStartAngle(double angle) {
440         this.startAngle = angle;
441         notifyListeners(new PlotChangeEvent(this));
442     }
443
444     /**
445      * Returns the maximum value any category axis can take.
446      *
447      * @return The maximum value.
448      */

449     public double getMaxValue() {
450         return this.maxValue;
451     }
452
453     /**
454      * Sets the maximum value any category axis can take and sends
455      * a {@link PlotChangeEvent} to all registered listeners.
456      *
457      * @param value the maximum value.
458      */

459     public void setMaxValue(double value) {
460         this.maxValue = value;
461         notifyListeners(new PlotChangeEvent(this));
462     }
463
464     /**
465      * Returns the direction in which the radar axes are drawn
466      * (clockwise or anti-clockwise).
467      *
468      * @return The direction (never <code>null</code>).
469      */

470     public Rotation getDirection() {
471         return this.direction;
472     }
473
474     /**
475      * Sets the direction in which the radar axes are drawn and sends a
476      * {@link PlotChangeEvent} to all registered listeners.
477      *
478      * @param direction the direction (<code>null</code> not permitted).
479      */

480     public void setDirection(Rotation direction) {
481         if (direction == null) {
482             throw new IllegalArgumentException JavaDoc("Null 'direction' argument.");
483         }
484         this.direction = direction;
485         notifyListeners(new PlotChangeEvent(this));
486     }
487
488     /**
489      * Returns the interior gap, measured as a percentage of the available
490      * drawing space.
491      *
492      * @return The gap (as a percentage of the available drawing space).
493      */

494     public double getInteriorGap() {
495         return this.interiorGap;
496     }
497
498     /**
499      * Sets the interior gap and sends a {@link PlotChangeEvent} to all
500      * registered listeners. This controls the space between the edges of the
501      * plot and the plot area itself (the region where the axis labels appear).
502      *
503      * @param percent the gap (as a percentage of the available drawing space).
504      */

505     public void setInteriorGap(double percent) {
506         if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
507             throw new IllegalArgumentException JavaDoc(
508                     "Percentage outside valid range.");
509         }
510         if (this.interiorGap != percent) {
511             this.interiorGap = percent;
512             notifyListeners(new PlotChangeEvent(this));
513         }
514     }
515
516     /**
517      * Returns the axis label gap.
518      *
519      * @return The axis label gap.
520      */

521     public double getAxisLabelGap() {
522         return this.axisLabelGap;
523     }
524     
525     /**
526      * Sets the axis label gap and sends a {@link PlotChangeEvent} to all
527      * registered listeners.
528      *
529      * @param gap the gap.
530      */

531     public void setAxisLabelGap(double gap) {
532         this.axisLabelGap = gap;
533         notifyListeners(new PlotChangeEvent(this));
534     }
535     
536     //// SERIES PAINT /////////////////////////
537

538     /**
539      * Returns the paint for ALL series in the plot.
540      *
541      * @return The paint (possibly <code>null</code>).
542      */

543     public Paint JavaDoc getSeriesPaint() {
544         return this.seriesPaint;
545     }
546
547     /**
548      * Sets the paint for ALL series in the plot. If this is set to</code> null
549      * </code>, then a list of paints is used instead (to allow different colors
550      * to be used for each series of the radar group).
551      *
552      * @param paint the paint (<code>null</code> permitted).
553      */

554     public void setSeriesPaint(Paint JavaDoc paint) {
555         this.seriesPaint = paint;
556         notifyListeners(new PlotChangeEvent(this));
557     }
558
559     /**
560      * Returns the paint for the specified series.
561      *
562      * @param series the series index (zero-based).
563      *
564      * @return The paint (never <code>null</code>).
565      */

566     public Paint JavaDoc getSeriesPaint(int series) {
567
568         // return the override, if there is one...
569
if (this.seriesPaint != null) {
570             return this.seriesPaint;
571         }
572
573         // otherwise look up the paint list
574
Paint JavaDoc result = this.seriesPaintList.getPaint(series);
575         if (result == null) {
576             DrawingSupplier supplier = getDrawingSupplier();
577             if (supplier != null) {
578                 Paint JavaDoc p = supplier.getNextPaint();
579                 this.seriesPaintList.setPaint(series, p);
580                 result = p;
581             }
582             else {
583                 result = this.baseSeriesPaint;
584             }
585         }
586         return result;
587
588     }
589
590     /**
591      * Sets the paint used to fill a series of the radar and sends a
592      * {@link PlotChangeEvent} to all registered listeners.
593      *
594      * @param series the series index (zero-based).
595      * @param paint the paint (<code>null</code> permitted).
596      */

597     public void setSeriesPaint(int series, Paint JavaDoc paint) {
598         this.seriesPaintList.setPaint(series, paint);
599         notifyListeners(new PlotChangeEvent(this));
600     }
601
602     /**
603      * Returns the base series paint. This is used when no other paint is
604      * available.
605      *
606      * @return The paint (never <code>null</code>).
607      */

608     public Paint JavaDoc getBaseSeriesPaint() {
609       return this.baseSeriesPaint;
610     }
611
612     /**
613      * Sets the base series paint.
614      *
615      * @param paint the paint (<code>null</code> not permitted).
616      */

617     public void setBaseSeriesPaint(Paint JavaDoc paint) {
618         if (paint == null) {
619             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
620         }
621         this.baseSeriesPaint = paint;
622         notifyListeners(new PlotChangeEvent(this));
623     }
624
625     //// SERIES OUTLINE PAINT ////////////////////////////
626

627     /**
628      * Returns the outline paint for ALL series in the plot.
629      *
630      * @return The paint (possibly <code>null</code>).
631      */

632     public Paint JavaDoc getSeriesOutlinePaint() {
633         return this.seriesOutlinePaint;
634     }
635
636     /**
637      * Sets the outline paint for ALL series in the plot. If this is set to
638      * </code> null</code>, then a list of paints is used instead (to allow
639      * different colors to be used for each series).
640      *
641      * @param paint the paint (<code>null</code> permitted).
642      */

643     public void setSeriesOutlinePaint(Paint JavaDoc paint) {
644         this.seriesOutlinePaint = paint;
645         notifyListeners(new PlotChangeEvent(this));
646     }
647
648     /**
649      * Returns the paint for the specified series.
650      *
651      * @param series the series index (zero-based).
652      *
653      * @return The paint (never <code>null</code>).
654      */

655     public Paint JavaDoc getSeriesOutlinePaint(int series) {
656         // return the override, if there is one...
657
if (this.seriesOutlinePaint != null) {
658             return this.seriesOutlinePaint;
659         }
660         // otherwise look up the paint list
661
Paint JavaDoc result = this.seriesOutlinePaintList.getPaint(series);
662         if (result == null) {
663             result = this.baseSeriesOutlinePaint;
664         }
665         return result;
666     }
667
668     /**
669      * Sets the paint used to fill a series of the radar and sends a
670      * {@link PlotChangeEvent} to all registered listeners.
671      *
672      * @param series the series index (zero-based).
673      * @param paint the paint (<code>null</code> permitted).
674      */

675     public void setSeriesOutlinePaint(int series, Paint JavaDoc paint) {
676         this.seriesOutlinePaintList.setPaint(series, paint);
677         notifyListeners(new PlotChangeEvent(this));
678     }
679
680     /**
681      * Returns the base series paint. This is used when no other paint is
682      * available.
683      *
684      * @return The paint (never <code>null</code>).
685      */

686     public Paint JavaDoc getBaseSeriesOutlinePaint() {
687         return this.baseSeriesOutlinePaint;
688     }
689
690     /**
691      * Sets the base series paint.
692      *
693      * @param paint the paint (<code>null</code> not permitted).
694      */

695     public void setBaseSeriesOutlinePaint(Paint JavaDoc paint) {
696         if (paint == null) {
697             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
698         }
699         this.baseSeriesOutlinePaint = paint;
700         notifyListeners(new PlotChangeEvent(this));
701     }
702
703     //// SERIES OUTLINE STROKE /////////////////////
704

705     /**
706      * Returns the outline stroke for ALL series in the plot.
707      *
708      * @return The stroke (possibly <code>null</code>).
709      */

710     public Stroke JavaDoc getSeriesOutlineStroke() {
711         return this.seriesOutlineStroke;
712     }
713
714     /**
715      * Sets the outline stroke for ALL series in the plot. If this is set to
716      * </code> null</code>, then a list of paints is used instead (to allow
717      * different colors to be used for each series).
718      *
719      * @param stroke the stroke (<code>null</code> permitted).
720      */

721     public void setSeriesOutlineStroke(Stroke JavaDoc stroke) {
722         this.seriesOutlineStroke = stroke;
723         notifyListeners(new PlotChangeEvent(this));
724     }
725
726     /**
727      * Returns the stroke for the specified series.
728      *
729      * @param series the series index (zero-based).
730      *
731      * @return The stroke (never <code>null</code>).
732      */

733     public Stroke JavaDoc getSeriesOutlineStroke(int series) {
734
735         // return the override, if there is one...
736
if (this.seriesOutlineStroke != null) {
737             return this.seriesOutlineStroke;
738         }
739
740         // otherwise look up the paint list
741
Stroke JavaDoc result = this.seriesOutlineStrokeList.getStroke(series);
742         if (result == null) {
743             result = this.baseSeriesOutlineStroke;
744         }
745         return result;
746
747     }
748
749     /**
750      * Sets the stroke used to fill a series of the radar and sends a
751      * {@link PlotChangeEvent} to all registered listeners.
752      *
753      * @param series the series index (zero-based).
754      * @param stroke the stroke (<code>null</code> permitted).
755      */

756     public void setSeriesOutlineStroke(int series, Stroke JavaDoc stroke) {
757         this.seriesOutlineStrokeList.setStroke(series, stroke);
758         notifyListeners(new PlotChangeEvent(this));
759     }
760
761     /**
762      * Returns the base series stroke. This is used when no other stroke is
763      * available.
764      *
765      * @return The stroke (never <code>null</code>).
766      */

767     public Stroke JavaDoc getBaseSeriesOutlineStroke() {
768         return this.baseSeriesOutlineStroke;
769     }
770
771     /**
772      * Sets the base series stroke.
773      *
774      * @param stroke the stroke (<code>null</code> not permitted).
775      */

776     public void setBaseSeriesOutlineStroke(Stroke JavaDoc stroke) {
777         if (stroke == null) {
778             throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
779         }
780         this.baseSeriesOutlineStroke = stroke;
781         notifyListeners(new PlotChangeEvent(this));
782     }
783
784     /**
785      * Returns the shape used for legend items.
786      *
787      * @return The shape.
788      */

789     public Shape JavaDoc getLegendItemShape() {
790         return this.legendItemShape;
791     }
792
793     /**
794      * Sets the shape used for legend items.
795      *
796      * @param shape the shape (<code>null</code> not permitted).
797      */

798     public void setLegendItemShape(Shape JavaDoc shape) {
799         if (shape == null) {
800             throw new IllegalArgumentException JavaDoc("Null 'shape' argument.");
801         }
802         this.legendItemShape = shape;
803         notifyListeners(new PlotChangeEvent(this));
804     }
805
806     /**
807      * Returns the series label font.
808      *
809      * @return The font (never <code>null</code>).
810      */

811     public Font JavaDoc getLabelFont() {
812         return this.labelFont;
813     }
814
815     /**
816      * Sets the series label font and sends a {@link PlotChangeEvent} to all
817      * registered listeners.
818      *
819      * @param font the font (<code>null</code> not permitted).
820      */

821     public void setLabelFont(Font JavaDoc font) {
822         if (font == null) {
823             throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
824         }
825         this.labelFont = font;
826         notifyListeners(new PlotChangeEvent(this));
827     }
828
829     /**
830      * Returns the series label paint.
831      *
832      * @return The paint (never <code>null</code>).
833      */

834     public Paint JavaDoc getLabelPaint() {
835         return this.labelPaint;
836     }
837
838     /**
839      * Sets the series label paint and sends a {@link PlotChangeEvent} to all
840      * registered listeners.
841      *
842      * @param paint the paint (<code>null</code> not permitted).
843      */

844     public void setLabelPaint(Paint JavaDoc paint) {
845         if (paint == null) {
846             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
847         }
848         this.labelPaint = paint;
849         notifyListeners(new PlotChangeEvent(this));
850     }
851
852     /**
853      * Returns the label generator.
854      *
855      * @return The label generator (never <code>null</code>).
856      */

857     public CategoryItemLabelGenerator getLabelGenerator() {
858         return this.labelGenerator;
859     }
860     
861     /**
862      * Sets the label generator and sends a {@link PlotChangeEvent} to all
863      * registered listeners.
864      *
865      * @param generator the generator (<code>null</code> not permitted).
866      */

867     public void setLabelGenerator(CategoryItemLabelGenerator generator) {
868         if (generator == null) {
869             throw new IllegalArgumentException JavaDoc("Null 'generator' argument.");
870         }
871         this.labelGenerator = generator;
872     }
873     
874     /**
875      * Returns the tool tip generator for the plot.
876      *
877      * @return The tool tip generator (possibly <code>null</code>).
878      *
879      * @see #setToolTipGenerator(CategoryToolTipGenerator)
880      *
881      * @since 1.0.2
882      */

883     public CategoryToolTipGenerator getToolTipGenerator() {
884         return this.toolTipGenerator;
885     }
886     
887     /**
888      * Sets the tool tip generator for the plot and sends a
889      * {@link PlotChangeEvent} to all registered listeners.
890      *
891      * @param generator the generator (<code>null</code> permitted).
892      *
893      * @see #getToolTipGenerator()
894      *
895      * @since 1.0.2
896      */

897     public void setToolTipGenerator(CategoryToolTipGenerator generator) {
898         this.toolTipGenerator = generator;
899         this.notifyListeners(new PlotChangeEvent(this));
900     }
901     
902     /**
903      * Returns the URL generator for the plot.
904      *
905      * @return The URL generator (possibly <code>null</code>).
906      *
907      * @see #setURLGenerator(CategoryURLGenerator)
908      *
909      * @since 1.0.2
910      */

911     public CategoryURLGenerator getURLGenerator() {
912         return this.urlGenerator;
913     }
914     
915     /**
916      * Sets the URL generator for the plot and sends a
917      * {@link PlotChangeEvent} to all registered listeners.
918      *
919      * @param generator the generator (<code>null</code> permitted).
920      *
921      * @see #getURLGenerator()
922      *
923      * @since 1.0.2
924      */

925     public void setURLGenerator(CategoryURLGenerator generator) {
926         this.urlGenerator = generator;
927         this.notifyListeners(new PlotChangeEvent(this));
928     }
929     
930     /**
931      * Returns a collection of legend items for the radar chart.
932      *
933      * @return The legend items.
934      */

935     public LegendItemCollection getLegendItems() {
936         LegendItemCollection result = new LegendItemCollection();
937
938         List JavaDoc keys = null;
939
940         if (this.dataExtractOrder == TableOrder.BY_ROW) {
941             keys = this.dataset.getRowKeys();
942         }
943         else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
944             keys = this.dataset.getColumnKeys();
945         }
946
947         if (keys != null) {
948             int series = 0;
949             Iterator JavaDoc iterator = keys.iterator();
950             Shape JavaDoc shape = getLegendItemShape();
951
952             while (iterator.hasNext()) {
953                 String JavaDoc label = iterator.next().toString();
954                 String JavaDoc description = label;
955
956                 Paint JavaDoc paint = getSeriesPaint(series);
957                 Paint JavaDoc outlinePaint = getSeriesOutlinePaint(series);
958                 Stroke JavaDoc stroke = getSeriesOutlineStroke(series);
959                 LegendItem item = new LegendItem(label, description,
960                         null, null, shape, paint, stroke, outlinePaint);
961                 result.add(item);
962                 series++;
963             }
964         }
965
966         return result;
967     }
968
969     /**
970      * Returns a cartesian point from a polar angle, length and bounding box
971      *
972      * @param bounds the area inside which the point needs to be.
973      * @param angle the polar angle, in degrees.
974      * @param length the relative length. Given in percent of maximum extend.
975      *
976      * @return The cartesian point.
977      */

978     protected Point2D JavaDoc getWebPoint(Rectangle2D JavaDoc bounds,
979                                   double angle, double length) {
980         
981         double angrad = Math.toRadians(angle);
982         double x = Math.cos(angrad) * length * bounds.getWidth() / 2;
983         double y = -Math.sin(angrad) * length * bounds.getHeight() / 2;
984
985         return new Point2D.Double JavaDoc(bounds.getX() + x + bounds.getWidth() / 2,
986                 bounds.getY() + y + bounds.getHeight() / 2);
987     }
988
989     /**
990      * Draws the plot on a Java 2D graphics device (such as the screen or a
991      * printer).
992      *
993      * @param g2 the graphics device.
994      * @param area the area within which the plot should be drawn.
995      * @param anchor the anchor point (<code>null</code> permitted).
996      * @param parentState the state from the parent plot, if there is one.
997      * @param info collects info about the drawing.
998      */

999     public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area, Point2D JavaDoc anchor,
1000                     PlotState parentState,
1001                     PlotRenderingInfo info)
1002    {
1003        // adjust for insets...
1004
RectangleInsets insets = getInsets();
1005        insets.trim(area);
1006
1007        if (info != null) {
1008            info.setPlotArea(area);
1009            info.setDataArea(area);
1010        }
1011
1012        drawBackground(g2, area);
1013        drawOutline(g2, area);
1014
1015        Shape JavaDoc savedClip = g2.getClip();
1016
1017        g2.clip(area);
1018        Composite JavaDoc originalComposite = g2.getComposite();
1019        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1020                getForegroundAlpha()));
1021
1022        if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
1023            int seriesCount = 0, catCount = 0;
1024
1025            if (this.dataExtractOrder == TableOrder.BY_ROW) {
1026                seriesCount = this.dataset.getRowCount();
1027                catCount = this.dataset.getColumnCount();
1028            }
1029            else {
1030                seriesCount = this.dataset.getColumnCount();
1031                catCount = this.dataset.getRowCount();
1032            }
1033
1034            // ensure we have a maximum value to use on the axes
1035
if (this.maxValue == DEFAULT_MAX_VALUE)
1036                calculateMaxValue(seriesCount, catCount);
1037
1038            // Next, setup the plot area
1039

1040            // adjust the plot area by the interior spacing value
1041

1042            double gapHorizontal = area.getWidth() * getInteriorGap();
1043            double gapVertical = area.getHeight() * getInteriorGap();
1044
1045            double X = area.getX() + gapHorizontal / 2;
1046            double Y = area.getY() + gapVertical / 2;
1047            double W = area.getWidth() - gapHorizontal;
1048            double H = area.getHeight() - gapVertical;
1049
1050            double headW = area.getWidth() * this.headPercent;
1051            double headH = area.getHeight() * this.headPercent;
1052
1053            // make the chart area a square
1054
double min = Math.min(W, H) / 2;
1055            X = (X + X + W) / 2 - min;
1056            Y = (Y + Y + H) / 2 - min;
1057            W = 2 * min;
1058            H = 2 * min;
1059
1060            Point2D JavaDoc centre = new Point2D.Double JavaDoc(X + W / 2, Y + H / 2);
1061            Rectangle2D JavaDoc radarArea = new Rectangle2D.Double JavaDoc(X, Y, W, H);
1062
1063            // Now actually plot each of the series polygons..
1064

1065            for (int series = 0; series < seriesCount; series++) {
1066                drawRadarPoly(g2, radarArea, centre, info, series, catCount,
1067                        headH, headW);
1068            }
1069        }
1070        else {
1071            drawNoDataMessage(g2, area);
1072        }
1073        g2.clip(savedClip);
1074        g2.setComposite(originalComposite);
1075        drawOutline(g2, area);
1076    }
1077
1078    /**
1079     * loop through each of the series to get the maximum value
1080     * on each category axis
1081     *
1082     * @param seriesCount the number of series
1083     * @param catCount the number of categories
1084     */

1085    private void calculateMaxValue(int seriesCount, int catCount) {
1086        double v = 0;
1087        Number JavaDoc nV = null;
1088
1089        for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) {
1090            for (int catIndex = 0; catIndex < catCount; catIndex++) {
1091                nV = getPlotValue(seriesIndex, catIndex);
1092                if (nV != null) {
1093                    v = nV.doubleValue();
1094                    if (v > this.maxValue) {
1095                        this.maxValue = v;
1096                    }
1097                }
1098            }
1099        }
1100    }
1101
1102    /**
1103     * Draws a radar plot polygon.
1104     *
1105     * @param g2 the graphics device.
1106     * @param plotArea the area we are plotting in (already adjusted).
1107     * @param centre the centre point of the radar axes
1108     * @param info chart rendering info.
1109     * @param series the series within the dataset we are plotting
1110     * @param catCount the number of categories per radar plot
1111     * @param headH the data point height
1112     * @param headW the data point width
1113     */

1114    protected void drawRadarPoly(Graphics2D JavaDoc g2,
1115                                 Rectangle2D JavaDoc plotArea,
1116                                 Point2D JavaDoc centre,
1117                                 PlotRenderingInfo info,
1118                                 int series, int catCount,
1119                                 double headH, double headW) {
1120
1121        Polygon JavaDoc polygon = new Polygon JavaDoc();
1122
1123        EntityCollection entities = null;
1124        if (info != null) {
1125            entities = info.getOwner().getEntityCollection();
1126        }
1127
1128        // plot the data...
1129
for (int cat = 0; cat < catCount; cat++) {
1130            Number JavaDoc dataValue = getPlotValue(series, cat);
1131
1132            if (dataValue != null) {
1133                double value = dataValue.doubleValue();
1134  
1135                if (value >= 0) { // draw the polygon series...
1136

1137                    // Finds our starting angle from the centre for this axis
1138

1139                    double angle = getStartAngle()
1140                        + (getDirection().getFactor() * cat * 360 / catCount);
1141
1142                    // The following angle calc will ensure there isn't a top
1143
// vertical axis - this may be useful if you don't want any
1144
// given criteria to 'appear' move important than the
1145
// others..
1146
// + (getDirection().getFactor()
1147
// * (cat + 0.5) * 360 / catCount);
1148

1149                    // find the point at the appropriate distance end point
1150
// along the axis/angle identified above and add it to the
1151
// polygon
1152

1153                    Point2D JavaDoc point = getWebPoint(plotArea, angle,
1154                            value / this.maxValue);
1155                    polygon.addPoint((int) point.getX(), (int) point.getY());
1156
1157                    // put an elipse at the point being plotted..
1158

1159                    Paint JavaDoc paint = getSeriesPaint(series);
1160                    Paint JavaDoc outlinePaint = getSeriesOutlinePaint(series);
1161                    Stroke JavaDoc outlineStroke = getSeriesOutlineStroke(series);
1162
1163                    Ellipse2D JavaDoc head = new Ellipse2D.Double JavaDoc(point.getX()
1164                            - headW / 2, point.getY() - headH / 2, headW,
1165                            headH);
1166                    g2.setPaint(paint);
1167                    g2.fill(head);
1168                    g2.setStroke(outlineStroke);
1169                    g2.setPaint(outlinePaint);
1170                    g2.draw(head);
1171
1172                    if (entities != null) {
1173                        String JavaDoc tip = null;
1174                        if (this.toolTipGenerator != null) {
1175                            tip = this.toolTipGenerator.generateToolTip(
1176                                    this.dataset, series, cat);
1177                        }
1178
1179                        String JavaDoc url = null;
1180                        if (this.urlGenerator != null) {
1181                            url = this.urlGenerator.generateURL(this.dataset,
1182                                   series, cat);
1183                        }
1184                   
1185                        Shape JavaDoc area = new Rectangle JavaDoc((int) (point.getX() - headW),
1186                                (int) (point.getY() - headH),
1187                                (int) (headW * 2), (int) (headH * 2));
1188                        CategoryItemEntity entity = new CategoryItemEntity(
1189                                area, tip, url, this.dataset, series,
1190                                dataset.getColumnKey(cat), cat);
1191                        entities.add(entity);
1192                    }
1193
1194                    // then draw the axis and category label, but only on the
1195
// first time through.....
1196

1197                    if (series == 0) {
1198                        Point2D JavaDoc endPoint = getWebPoint(plotArea, angle, 1);
1199                                                             // 1 = end of axis
1200
Line2D JavaDoc line = new Line2D.Double JavaDoc(centre, endPoint);
1201                        g2.draw(line);
1202                        drawLabel(g2, plotArea, value, cat, angle,
1203                                360.0 / catCount);
1204                    }
1205                }
1206            }
1207        }
1208        // Plot the polygon
1209

1210        Paint JavaDoc paint = getSeriesPaint(series);
1211        g2.setPaint(paint);
1212        g2.draw(polygon);
1213
1214        // Lastly, fill the web polygon if this is required
1215

1216        if (this.webFilled) {
1217            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1218                    0.1f));
1219            g2.fill(polygon);
1220            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1221                    getForegroundAlpha()));
1222        }
1223    }
1224
1225    /**
1226     * Returns the value to be plotted at the interseries of the
1227     * series and the category. This allows us to plot
1228     * BY_ROW or BY_COLUMN which basically is just reversing the
1229     * definition of the categories and data series being plotted
1230     *
1231     * @param series the series to be plotted
1232     * @param cat the category within the series to be plotted
1233     *
1234     * @return The value to be plotted
1235     */

1236    Number JavaDoc getPlotValue(int series, int cat) {
1237        Number JavaDoc value = null;
1238        if (this.dataExtractOrder == TableOrder.BY_ROW) {
1239            value = this.dataset.getValue(series, cat);
1240        }
1241        else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
1242            value = this.dataset.getValue(cat, series);
1243        }
1244        return value;
1245    }
1246
1247    /**
1248     * Draws the label for one axis.
1249     *
1250     * @param g2 the graphics device.
1251     * @param plotArea the plot area
1252     * @param value the value of the label.
1253     * @param cat the category (zero-based index).
1254     * @param startAngle the starting angle.
1255     * @param extent the extent of the arc.
1256     */

1257    protected void drawLabel(Graphics2D JavaDoc g2, Rectangle2D JavaDoc plotArea, double value,
1258                             int cat, double startAngle, double extent) {
1259        FontRenderContext JavaDoc frc = g2.getFontRenderContext();
1260 
1261        String JavaDoc label = null;
1262        if (this.dataExtractOrder == TableOrder.BY_ROW) {
1263            // if series are in rows, then the categories are the column keys
1264
label = this.labelGenerator.generateColumnLabel(this.dataset, cat);
1265        }
1266        else {
1267            // if series are in columns, then the categories are the row keys
1268
label = this.labelGenerator.generateRowLabel(this.dataset, cat);
1269        }
1270 
1271        Rectangle2D JavaDoc labelBounds = getLabelFont().getStringBounds(label, frc);
1272        LineMetrics JavaDoc lm = getLabelFont().getLineMetrics(label, frc);
1273        double ascent = lm.getAscent();
1274
1275        Point2D JavaDoc labelLocation = calculateLabelLocation(labelBounds, ascent,
1276                plotArea, startAngle);
1277
1278        Composite JavaDoc saveComposite = g2.getComposite();
1279    
1280        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
1281                1.0f));
1282        g2.setPaint(getLabelPaint());
1283        g2.setFont(getLabelFont());
1284        g2.drawString(label, (float) labelLocation.getX(),
1285                (float) labelLocation.getY());
1286        g2.setComposite(saveComposite);
1287    }
1288
1289    /**
1290     * Returns the location for a label
1291     *
1292     * @param labelBounds the label bounds.
1293     * @param ascent the ascent (height of font).
1294     * @param plotArea the plot area
1295     * @param startAngle the start angle for the pie series.
1296     *
1297     * @return The location for a label.
1298     */

1299    protected Point2D JavaDoc calculateLabelLocation(Rectangle2D JavaDoc labelBounds,
1300                                             double ascent,
1301                                             Rectangle2D JavaDoc plotArea,
1302                                             double startAngle)
1303    {
1304        Arc2D JavaDoc arc1 = new Arc2D.Double JavaDoc(plotArea, startAngle, 0, Arc2D.OPEN);
1305        Point2D JavaDoc point1 = arc1.getEndPoint();
1306
1307        double deltaX = -(point1.getX() - plotArea.getCenterX())
1308                        * this.axisLabelGap;
1309        double deltaY = -(point1.getY() - plotArea.getCenterY())
1310                        * this.axisLabelGap;
1311
1312        double labelX = point1.getX() - deltaX;
1313        double labelY = point1.getY() - deltaY;
1314
1315        if (labelX < plotArea.getCenterX()) {
1316            labelX -= labelBounds.getWidth();
1317        }
1318    
1319        if (labelX == plotArea.getCenterX()) {
1320            labelX -= labelBounds.getWidth() / 2;
1321        }
1322
1323        if (labelY > plotArea.getCenterY()) {
1324            labelY += ascent;
1325        }
1326
1327        return new Point2D.Double JavaDoc(labelX, labelY);
1328    }
1329    
1330    /**
1331     * Tests this plot for equality with an arbitrary object.
1332     *
1333     * @param obj the object (<code>null</code> permitted).
1334     *
1335     * @return A boolean.
1336     */

1337    public boolean equals(Object JavaDoc obj) {
1338        if (obj == this) {
1339            return true;
1340        }
1341        if (!(obj instanceof SpiderWebPlot)) {
1342            return false;
1343        }
1344        if (!super.equals(obj)) {
1345            return false;
1346        }
1347        SpiderWebPlot that = (SpiderWebPlot) obj;
1348        if (!this.dataExtractOrder.equals(that.dataExtractOrder)) {
1349            return false;
1350        }
1351        if (this.headPercent != that.headPercent) {
1352            return false;
1353        }
1354        if (this.interiorGap != that.interiorGap) {
1355            return false;
1356        }
1357        if (this.startAngle != that.startAngle) {
1358            return false;
1359        }
1360        if (!this.direction.equals(that.direction)) {
1361            return false;
1362        }
1363        if (this.maxValue != that.maxValue) {
1364            return false;
1365        }
1366        if (this.webFilled != that.webFilled) {
1367            return false;
1368        }
1369        if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
1370            return false;
1371        }
1372        if (!PaintUtilities.equal(this.seriesPaint, that.seriesPaint)) {
1373            return false;
1374        }
1375        if (!this.seriesPaintList.equals(that.seriesPaintList)) {
1376            return false;
1377        }
1378        if (!PaintUtilities.equal(this.baseSeriesPaint, that.baseSeriesPaint)) {
1379            return false;
1380        }
1381        if (!PaintUtilities.equal(this.seriesOutlinePaint,
1382                that.seriesOutlinePaint)) {
1383            return false;
1384        }
1385        if (!this.seriesOutlinePaintList.equals(that.seriesOutlinePaintList)) {
1386            return false;
1387        }
1388        if (!PaintUtilities.equal(this.baseSeriesOutlinePaint,
1389                that.baseSeriesOutlinePaint)) {
1390            return false;
1391        }
1392        if (!ObjectUtilities.equal(this.seriesOutlineStroke,
1393                that.seriesOutlineStroke)) {
1394            return false;
1395        }
1396        if (!this.seriesOutlineStrokeList.equals(
1397                that.seriesOutlineStrokeList)) {
1398            return false;
1399        }
1400        if (!this.baseSeriesOutlineStroke.equals(
1401                that.baseSeriesOutlineStroke)) {
1402            return false;
1403        }
1404        if (!this.labelFont.equals(that.labelFont)) {
1405            return false;
1406        }
1407        if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
1408            return false;
1409        }
1410        if (!this.labelGenerator.equals(that.labelGenerator)) {
1411            return false;
1412        }
1413        if (!ObjectUtilities.equal(this.toolTipGenerator,
1414                that.toolTipGenerator)) {
1415            return false;
1416        }
1417        if (!ObjectUtilities.equal(this.urlGenerator,
1418                that.urlGenerator)) {
1419            return false;
1420        }
1421        return true;
1422    }
1423    
1424    /**
1425     * Provides serialization support.
1426     *
1427     * @param stream the output stream.
1428     *
1429     * @throws IOException if there is an I/O error.
1430     */

1431    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
1432        stream.defaultWriteObject();
1433
1434        SerialUtilities.writeShape(this.legendItemShape, stream);
1435        SerialUtilities.writePaint(this.seriesPaint, stream);
1436        SerialUtilities.writePaint(this.baseSeriesPaint, stream);
1437        SerialUtilities.writePaint(this.seriesOutlinePaint, stream);
1438        SerialUtilities.writePaint(this.baseSeriesOutlinePaint, stream);
1439        SerialUtilities.writeStroke(this.seriesOutlineStroke, stream);
1440        SerialUtilities.writeStroke(this.baseSeriesOutlineStroke, stream);
1441        SerialUtilities.writePaint(this.labelPaint, stream);
1442    }
1443
1444    /**
1445     * Provides serialization support.
1446     *
1447     * @param stream the input stream.
1448     *
1449     * @throws IOException if there is an I/O error.
1450     * @throws ClassNotFoundException if there is a classpath problem.
1451     */

1452    private void readObject(ObjectInputStream JavaDoc stream) throws IOException JavaDoc,
1453            ClassNotFoundException JavaDoc {
1454        stream.defaultReadObject();
1455
1456        this.legendItemShape = SerialUtilities.readShape(stream);
1457        this.seriesPaint = SerialUtilities.readPaint(stream);
1458        this.baseSeriesPaint = SerialUtilities.readPaint(stream);
1459        this.seriesOutlinePaint = SerialUtilities.readPaint(stream);
1460        this.baseSeriesOutlinePaint = SerialUtilities.readPaint(stream);
1461        this.seriesOutlineStroke = SerialUtilities.readStroke(stream);
1462        this.baseSeriesOutlineStroke = SerialUtilities.readStroke(stream);
1463        this.labelPaint = SerialUtilities.readPaint(stream);
1464       
1465        if (dataset != null) {
1466            dataset.addChangeListener(this);
1467        }
1468    }
1469
1470}
1471
Popular Tags