KickJava   Java API By Example, From Geeks To Geeks.

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


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  * PiePlot.java
28  * ------------
29  * (C) Copyright 2000-2005, by Andrzej Porebski and Contributors.
30  *
31  * Original Author: Andrzej Porebski;
32  * Contributor(s): David Gilbert (for Object Refinery Limited);
33  * Martin Cordova (percentages in labels);
34  * Richard Atkinson (URL support for image maps);
35  * Christian W. Zuckschwerdt;
36  * Arnaud Lelievre;
37  * Andreas Schroeder (very minor);
38  *
39  * $Id: PiePlot.java,v 1.17 2005/05/19 14:03:41 mungady Exp $
40  *
41  * Changes (from 21-Jun-2001)
42  * --------------------------
43  * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
44  * 18-Sep-2001 : Updated header (DG);
45  * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
46  * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to
47  * Plot.java (DG);
48  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
49  * 13-Nov-2001 : Modified plot subclasses so that null axes are possible for
50  * pie plot (DG);
51  * 17-Nov-2001 : Added PieDataset interface and amended this class accordingly,
52  * and completed removal of BlankAxis class as it is no longer
53  * required (DG);
54  * 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG);
55  * 21-Nov-2001 : Added options for exploding pie sections and filled out range
56  * of properties (DG);
57  * Added option for percentages in chart labels, based on code
58  * by Martin Cordova (DG);
59  * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
60  * 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG);
61  * 13-Dec-2001 : Added tooltips (DG);
62  * 16-Jan-2002 : Renamed tooltips class (DG);
63  * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
64  * 05-Feb-2002 : Added alpha-transparency to plot class, and updated
65  * constructors accordingly (DG);
66  * 06-Feb-2002 : Added optional background image and alpha-transparency to Plot
67  * and subclasses. Clipped drawing within plot area (DG);
68  * 26-Mar-2002 : Added an empty zoom method (DG);
69  * 18-Apr-2002 : PieDataset is no longer sorted (oldman);
70  * 23-Apr-2002 : Moved dataset from JFreeChart to Plot. Added
71  * getLegendItemLabels() method (DG);
72  * 19-Jun-2002 : Added attributes to control starting angle and direction
73  * (default is now clockwise) (DG);
74  * 25-Jun-2002 : Removed redundant imports (DG);
75  * 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG);
76  * 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG);
77  * 30-Jul-2002 : Moved summation code to DatasetUtilities (DG);
78  * 05-Aug-2002 : Added URL support for image maps - new member variable for
79  * urlGenerator, modified constructor and minor change to the
80  * draw method (RA);
81  * 18-Sep-2002 : Modified the percent label creation and added setters for the
82  * formatters (AS);
83  * 24-Sep-2002 : Added getLegendItems() method (DG);
84  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
85  * 09-Oct-2002 : Added check for null entity collection (DG);
86  * 30-Oct-2002 : Changed PieDataset interface (DG);
87  * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
88  * 02-Jan-2003 : Fixed "no data" message (DG);
89  * 23-Jan-2003 : Modified to extract data from rows OR columns in
90  * CategoryDataset (DG);
91  * 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply
92  * (bug id 685536) (DG);
93  * 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip
94  * and URL generators (DG);
95  * 21-Mar-2003 : Added a minimum angle for drawing arcs
96  * (see bug id 620031) (DG);
97  * 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG);
98  * 02-Jun-2003 : Fixed bug 721733 (DG);
99  * 30-Jul-2003 : Modified entity constructor (CZ);
100  * 19-Aug-2003 : Implemented Cloneable (DG);
101  * 29-Aug-2003 : Fixed bug 796936 (null pointer on setOutlinePaint()) (DG);
102  * 08-Sep-2003 : Added internationalization via use of properties
103  * resourceBundle (RFE 690236) (AL);
104  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
105  * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
106  * 05-Nov-2003 : Fixed missing legend bug (DG);
107  * 10-Nov-2003 : Re-added the DatasetChangeListener to constructors (CZ);
108  * 29-Jan-2004 : Fixed clipping bug in draw() method (DG);
109  * 11-Mar-2004 : Major overhaul to improve labelling (DG);
110  * 31-Mar-2004 : Made an adjustment for the plot area when the label generator
111  * is null. Fixed null pointer exception when the label
112  * generator returns null for a label (DG);
113  * 06-Apr-2004 : Added getter, setter, serialization and draw support for
114  * labelBackgroundPaint (AS);
115  * 08-Apr-2004 : Added flag to control whether null values are ignored or
116  * not (DG);
117  * 15-Apr-2004 : Fixed some minor warnings from Eclipse (DG);
118  * 26-Apr-2004 : Added attributes for label outline and shadow (DG);
119  * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
120  * 04-Nov-2004 : Fixed null pointer exception with new LegendTitle class (DG);
121  * 09-Nov-2004 : Added user definable legend item shape (DG);
122  * 25-Nov-2004 : Added new legend label generator (DG);
123  * 20-Apr-2005 : Added a tool tip generator for legend labels (DG);
124  * 26-Apr-2005 : Removed LOGGER (DG);
125  * 05-May-2005 : Updated draw() method parameters (DG);
126  * 10-May-2005 : Added flag to control visibility of label linking lines, plus
127  * another flag to control the handling of zero values (DG);
128  *
129  */

130
131 package org.jfree.chart.plot;
132
133 import java.awt.AlphaComposite JavaDoc;
134 import java.awt.BasicStroke JavaDoc;
135 import java.awt.Color JavaDoc;
136 import java.awt.Composite JavaDoc;
137 import java.awt.Font JavaDoc;
138 import java.awt.Graphics2D JavaDoc;
139 import java.awt.Paint JavaDoc;
140 import java.awt.Shape JavaDoc;
141 import java.awt.Stroke JavaDoc;
142 import java.awt.geom.Arc2D JavaDoc;
143 import java.awt.geom.Line2D JavaDoc;
144 import java.awt.geom.Point2D JavaDoc;
145 import java.awt.geom.Rectangle2D JavaDoc;
146 import java.io.IOException JavaDoc;
147 import java.io.ObjectInputStream JavaDoc;
148 import java.io.ObjectOutputStream JavaDoc;
149 import java.io.Serializable JavaDoc;
150 import java.util.Iterator JavaDoc;
151 import java.util.List JavaDoc;
152 import java.util.ResourceBundle JavaDoc;
153
154 import org.jfree.chart.LegendItem;
155 import org.jfree.chart.LegendItemCollection;
156 import org.jfree.chart.entity.EntityCollection;
157 import org.jfree.chart.entity.PieSectionEntity;
158 import org.jfree.chart.event.PlotChangeEvent;
159 import org.jfree.chart.labels.PieSectionLabelGenerator;
160 import org.jfree.chart.labels.PieToolTipGenerator;
161 import org.jfree.chart.labels.StandardPieItemLabelGenerator;
162 import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
163 import org.jfree.chart.urls.PieURLGenerator;
164 import org.jfree.data.DefaultKeyedValues;
165 import org.jfree.data.KeyedValues;
166 import org.jfree.data.general.DatasetChangeEvent;
167 import org.jfree.data.general.DatasetUtilities;
168 import org.jfree.data.general.PieDataset;
169 import org.jfree.io.SerialUtilities;
170 import org.jfree.text.G2TextMeasurer;
171 import org.jfree.text.TextBlock;
172 import org.jfree.text.TextBox;
173 import org.jfree.text.TextUtilities;
174 import org.jfree.ui.RectangleAnchor;
175 import org.jfree.ui.RectangleInsets;
176 import org.jfree.util.ObjectList;
177 import org.jfree.util.ObjectUtilities;
178 import org.jfree.util.PaintList;
179 import org.jfree.util.Rotation;
180 import org.jfree.util.ShapeUtilities;
181 import org.jfree.util.StrokeList;
182
183 /**
184  * A plot that displays data in the form of a pie chart, using data from any
185  * class that implements the {@link PieDataset} interface.
186  * <P>
187  * Special notes:
188  * <ol>
189  * <li>the default starting point is 12 o'clock and the pie sections proceed
190  * in a clockwise direction, but these settings can be changed;</li>
191  * <li>negative values in the dataset are ignored;</li>
192  * <li>there are utility methods for creating a {@link PieDataset} from a
193  * {@link org.jfree.data.category.CategoryDataset};</li>
194  * </ol>
195  *
196  * @see Plot
197  * @see PieDataset
198  */

199 public class PiePlot extends Plot implements Cloneable JavaDoc, Serializable JavaDoc {
200     
201     /** For serialization. */
202     private static final long serialVersionUID = -795612466005590431L;
203     
204     /** The default interior gap. */
205     public static final double DEFAULT_INTERIOR_GAP = 0.25;
206
207     /** The maximum interior gap (currently 40%). */
208     public static final double MAX_INTERIOR_GAP = 0.40;
209
210     /** The default starting angle for the pie chart. */
211     public static final double DEFAULT_START_ANGLE = 90.0;
212
213     /** The default section label font. */
214     public static final Font JavaDoc DEFAULT_LABEL_FONT
215         = new Font JavaDoc("SansSerif", Font.PLAIN, 10);
216
217     /** The default section label paint. */
218     public static final Paint JavaDoc DEFAULT_LABEL_PAINT = Color.black;
219     
220     /** The default section label background paint. */
221     public static final Paint JavaDoc DEFAULT_LABEL_BACKGROUND_PAINT
222         = new Color JavaDoc(255, 255, 192);
223
224     /** The default section label outline paint. */
225     public static final Paint JavaDoc DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
226     
227     /** The default section label outline stroke. */
228     public static final Stroke JavaDoc DEFAULT_LABEL_OUTLINE_STROKE
229         = new BasicStroke JavaDoc(0.5f);
230     
231     /** The default section label shadow paint. */
232     public static final Paint JavaDoc DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray;
233     
234     /** The default minimum arc angle to draw. */
235     public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
236
237     /** The dataset for the pie chart. */
238     private PieDataset dataset;
239
240     /** The pie index (used by the {@link MultiplePiePlot} class). */
241     private int pieIndex;
242
243     /**
244      * The amount of space left around the outside of the pie plot, expressed
245      * as a percentage.
246      */

247     private double interiorGap;
248
249     /** Flag determining whether to draw an ellipse or a perfect circle. */
250     private boolean circular;
251
252     /** The starting angle. */
253     private double startAngle;
254
255     /** The direction for the pie segments. */
256     private Rotation direction;
257
258     /** The paint for ALL sections (overrides list). */
259     private transient Paint JavaDoc sectionPaint;
260
261     /** The section paint list. */
262     private PaintList sectionPaintList;
263
264     /** The base section paint (fallback). */
265     private transient Paint JavaDoc baseSectionPaint;
266
267     /** The outline paint for ALL sections (overrides list). */
268     private transient Paint JavaDoc sectionOutlinePaint;
269
270     /** The section outline paint list. */
271     private PaintList sectionOutlinePaintList;
272
273     /** The base section outline paint (fallback). */
274     private transient Paint JavaDoc baseSectionOutlinePaint;
275
276     /** The outline stroke for ALL sections (overrides list). */
277     private transient Stroke JavaDoc sectionOutlineStroke;
278
279     /** The section outline stroke list. */
280     private StrokeList sectionOutlineStrokeList;
281
282     /** The base section outline stroke (fallback). */
283     private transient Stroke JavaDoc baseSectionOutlineStroke;
284
285     /** The shadow paint. */
286     private transient Paint JavaDoc shadowPaint = Color.gray;
287
288     /** The x-offset for the shadow effect. */
289     private double shadowXOffset = 4.0f;
290     
291     /** The y-offset for the shadow effect. */
292     private double shadowYOffset = 4.0f;
293     
294     /** The percentage amount to explode each pie section. */
295     private ObjectList explodePercentages;
296     
297     /** The section label generator. */
298     private PieSectionLabelGenerator labelGenerator;
299
300     /** The font used to display the section labels. */
301     private Font JavaDoc labelFont;
302
303     /** The color used to draw the section labels. */
304     private transient Paint JavaDoc labelPaint;
305     
306     /** The color used to draw the background of the section labels. */
307     private transient Paint JavaDoc labelBackgroundPaint;
308
309     /**
310      * The paint used to draw the outline of the section labels
311      * (<code>null</code> permitted).
312      */

313     private transient Paint JavaDoc labelOutlinePaint;
314     
315     /**
316      * The stroke used to draw the outline of the section labels
317      * (<code>null</code> permitted).
318      */

319     private transient Stroke JavaDoc labelOutlineStroke;
320     
321     /**
322      * The paint used to draw the shadow for the section labels
323      * (<code>null</code> permitted).
324      */

325     private transient Paint JavaDoc labelShadowPaint;
326     
327     /** The maximum label width as a percentage of the plot width. */
328     private double maximumLabelWidth = 0.20;
329     
330     /**
331      * The gap between the labels and the plot as a percentage of the plot
332      * width.
333      */

334     private double labelGap = 0.05;
335
336     /** A flag that controls whether or not the label links are drawn. */
337     private boolean labelLinksVisible;
338     
339     /** The link margin. */
340     private double labelLinkMargin = 0.05;
341     
342     /** The paint used for the label linking lines. */
343     private transient Paint JavaDoc labelLinkPaint = Color.black;
344     
345     /** The stroke used for the label linking lines. */
346     private transient Stroke JavaDoc labelLinkStroke = new BasicStroke JavaDoc(0.5f);
347     
348     /** The tooltip generator. */
349     private PieToolTipGenerator toolTipGenerator;
350
351     /** The URL generator. */
352     private PieURLGenerator urlGenerator;
353     
354     /** The legend label generator. */
355     private PieSectionLabelGenerator legendLabelGenerator;
356     
357     /** A tool tip generator for the legend. */
358     private PieSectionLabelGenerator legendLabelToolTipGenerator;
359     
360     /**
361      * A flag that controls whether <code>null</code> values are ignored.
362      */

363     private boolean ignoreNullValues;
364     
365     /**
366      * A flag that controls whether zero values are ignored.
367      */

368     private boolean ignoreZeroValues;
369
370     /** The legend item shape. */
371     private transient Shape JavaDoc legendItemShape;
372     
373     /**
374      * The smallest arc angle that will get drawn (this is to avoid a bug in
375      * various Java implementations that causes the JVM to crash). See this
376      * link for details:
377      *
378      * http://www.jfree.org/phpBB2/viewtopic.php?t=2707
379      *
380      * ...and this bug report in the Java Bug Parade:
381      *
382      * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html
383      */

384     private double minimumArcAngleToDraw;
385
386     /** The resourceBundle for the localization. */
387     protected static ResourceBundle JavaDoc localizationResources =
388         ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
389
390     /**
391      * Creates a new plot. The dataset is initially set to <code>null</code>.
392      */

393     public PiePlot() {
394         this(null);
395     }
396
397     /**
398      * Creates a plot that will draw a pie chart for the specified dataset.
399      *
400      * @param dataset the dataset (<code>null</code> permitted).
401      */

402     public PiePlot(PieDataset dataset) {
403         super();
404         this.dataset = dataset;
405         if (dataset != null) {
406             dataset.addChangeListener(this);
407         }
408         this.pieIndex = 0;
409         
410         this.interiorGap = DEFAULT_INTERIOR_GAP;
411         this.circular = true;
412         this.startAngle = DEFAULT_START_ANGLE;
413         this.direction = Rotation.CLOCKWISE;
414         this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
415         
416         this.sectionPaint = null;
417         this.sectionPaintList = new PaintList();
418         this.baseSectionPaint = null;
419
420         this.sectionOutlinePaint = null;
421         this.sectionOutlinePaintList = new PaintList();
422         this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
423
424         this.sectionOutlineStroke = null;
425         this.sectionOutlineStrokeList = new StrokeList();
426         this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
427         
428         this.explodePercentages = new ObjectList();
429
430         this.labelGenerator = new StandardPieItemLabelGenerator();
431         this.labelFont = DEFAULT_LABEL_FONT;
432         this.labelPaint = DEFAULT_LABEL_PAINT;
433         this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
434         this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
435         this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
436         this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
437         this.labelLinksVisible = true;
438         
439         this.toolTipGenerator = null;
440         this.urlGenerator = null;
441         this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
442         this.legendLabelToolTipGenerator = null;
443         this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
444         
445         this.ignoreNullValues = false;
446         this.ignoreZeroValues = false;
447     }
448
449     /**
450      * Returns the dataset.
451      *
452      * @return The dataset (possibly <code>null</code>).
453      */

454     public PieDataset getDataset() {
455         return this.dataset;
456     }
457
458     /**
459      * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'.
460      *
461      * @param dataset the dataset (<code>null</code> permitted).
462      */

463     public void setDataset(PieDataset dataset) {
464         // if there is an existing dataset, remove the plot from the list of
465
// change listeners...
466
PieDataset existing = this.dataset;
467         if (existing != null) {
468             existing.removeChangeListener(this);
469         }
470
471         // set the new dataset, and register the chart as a change listener...
472
this.dataset = dataset;
473         if (dataset != null) {
474             setDatasetGroup(dataset.getGroup());
475             dataset.addChangeListener(this);
476         }
477
478         // send a dataset change event to self...
479
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
480         datasetChanged(event);
481     }
482     
483     /**
484      * Returns the pie index (this is used by the {@link MultiplePiePlot} class
485      * to track subplots).
486      *
487      * @return The pie index.
488      */

489     public int getPieIndex() {
490         return this.pieIndex;
491     }
492     
493     /**
494      * Sets the pie index (this is used by the {@link MultiplePiePlot} class to
495      * track subplots).
496      *
497      * @param index the index.
498      */

499     public void setPieIndex(int index) {
500         this.pieIndex = index;
501     }
502     
503     /**
504      * Returns the start angle for the first pie section. This is measured in
505      * degrees starting from 3 o'clock and measuring anti-clockwise.
506      *
507      * @return The start angle.
508      */

509     public double getStartAngle() {
510         return this.startAngle;
511     }
512
513     /**
514      * Sets the starting angle and sends a {@link PlotChangeEvent} to all
515      * registered listeners. The initial default value is 90 degrees, which
516      * corresponds to 12 o'clock. A value of zero corresponds to 3 o'clock...
517      * this is the encoding used by Java's Arc2D class.
518      *
519      * @param angle the angle (in degrees).
520      */

521     public void setStartAngle(double angle) {
522         this.startAngle = angle;
523         notifyListeners(new PlotChangeEvent(this));
524     }
525
526     /**
527      * Returns the direction in which the pie sections are drawn (clockwise or
528      * anti-clockwise).
529      *
530      * @return The direction (never <code>null</code>).
531      */

532     public Rotation getDirection() {
533         return this.direction;
534     }
535
536     /**
537      * Sets the direction in which the pie sections are drawn and sends a
538      * {@link PlotChangeEvent} to all registered listeners.
539      *
540      * @param direction the direction (<code>null</code> not permitted).
541      */

542     public void setDirection(Rotation direction) {
543         if (direction == null) {
544             throw new IllegalArgumentException JavaDoc("Null 'direction' argument.");
545         }
546         this.direction = direction;
547         notifyListeners(new PlotChangeEvent(this));
548
549     }
550
551     /**
552      * Returns the interior gap, measured as a percentage of the available
553      * drawing space.
554      *
555      * @return The gap (as a percentage of the available drawing space).
556      */

557     public double getInteriorGap() {
558         return this.interiorGap;
559     }
560
561     /**
562      * Sets the interior gap and sends a {@link PlotChangeEvent} to all
563      * registered listeners. This controls the space between the edges of the
564      * pie plot and the plot area itself (the region where the section labels
565      * appear).
566      *
567      * @param percent the gap (as a percentage of the available drawing space).
568      */

569     public void setInteriorGap(double percent) {
570
571         // check arguments...
572
if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
573             throw new IllegalArgumentException JavaDoc(
574                 "Invalid 'percent' (" + percent + ") argument.");
575         }
576
577         // make the change...
578
if (this.interiorGap != percent) {
579             this.interiorGap = percent;
580             notifyListeners(new PlotChangeEvent(this));
581         }
582
583     }
584
585     /**
586      * Returns a flag indicating whether the pie chart is circular, or
587      * stretched into an elliptical shape.
588      *
589      * @return A flag indicating whether the pie chart is circular.
590      */

591     public boolean isCircular() {
592         return this.circular;
593     }
594
595     /**
596      * A flag indicating whether the pie chart is circular, or stretched into
597      * an elliptical shape.
598      *
599      * @param flag the new value.
600      */

601     public void setCircular(boolean flag) {
602         setCircular(flag, true);
603     }
604
605     /**
606      * Sets the circular attribute and, if requested, sends a
607      * {@link PlotChangeEvent} to all registered listeners.
608      *
609      * @param circular the new value of the flag.
610      * @param notify notify listeners?
611      */

612     public void setCircular(boolean circular, boolean notify) {
613         this.circular = circular;
614         if (notify) {
615             notifyListeners(new PlotChangeEvent(this));
616         }
617     }
618
619     /**
620      * Returns the flag that controls whether <code>null</code> values in the
621      * dataset are ignored.
622      *
623      * @return A boolean.
624      */

625     public boolean getIgnoreNullValues() {
626         return this.ignoreNullValues;
627     }
628     
629     /**
630      * Sets a flag that controls whether <code>null</code> values are ignored,
631      * and sends a {@link PlotChangeEvent} to all registered listeners. At
632      * present, this only affects whether or not the key is presented in the
633      * legend.
634      *
635      * @param flag the flag.
636      */

637     public void setIgnoreNullValues(boolean flag) {
638         this.ignoreNullValues = flag;
639         notifyListeners(new PlotChangeEvent(this));
640     }
641     
642     /**
643      * Returns the flag that controls whether zero values in the
644      * dataset are ignored.
645      *
646      * @return A boolean.
647      */

648     public boolean getIgnoreZeroValues() {
649         return this.ignoreZeroValues;
650     }
651     
652     /**
653      * Sets a flag that controls whether zero values are ignored,
654      * and sends a {@link PlotChangeEvent} to all registered listeners. This
655      * only affects whether or not a label appears for the non-visible
656      * pie section.
657      *
658      * @param flag the flag.
659      */

660     public void setIgnoreZeroValues(boolean flag) {
661         this.ignoreZeroValues = flag;
662         notifyListeners(new PlotChangeEvent(this));
663     }
664     
665     //// SECTION PAINT ////////////////////////////////////////////////////////
666

667     /**
668      * Returns the paint for ALL sections in the plot.
669      *
670      * @return The paint (possibly <code>null</code>).
671      */

672     public Paint JavaDoc getSectionPaint() {
673         return this.sectionPaint;
674     }
675
676     /**
677      * Sets the paint for ALL sections in the plot. If this is set to
678      * </code>null</code>, then a list of paints is used instead (to allow
679      * different colors to be used for each section).
680      *
681      * @param paint the paint (<code>null</code> permitted).
682      */

683     public void setSectionPaint(Paint JavaDoc paint) {
684         this.sectionPaint = paint;
685         notifyListeners(new PlotChangeEvent(this));
686     }
687
688     /**
689      * Returns the paint for the specified section.
690      *
691      * @param section the section index (zero-based).
692      *
693      * @return The paint (never <code>null</code>).
694      */

695     public Paint JavaDoc getSectionPaint(int section) {
696         
697         // return the override, if there is one...
698
if (this.sectionPaint != null) {
699             return this.sectionPaint;
700         }
701
702         // otherwise look up the paint list
703
Paint JavaDoc result = this.sectionPaintList.getPaint(section);
704         if (result == null) {
705             DrawingSupplier supplier = getDrawingSupplier();
706             if (supplier != null) {
707                 Paint JavaDoc p = supplier.getNextPaint();
708                 this.sectionPaintList.setPaint(section, p);
709                 result = p;
710             }
711             else {
712                 result = this.baseSectionPaint;
713             }
714         }
715         return result;
716        
717     }
718     
719     /**
720      * Sets the paint used to fill a section of the pie and sends a
721      * {@link PlotChangeEvent} to all registered listeners.
722      *
723      * @param section the section index (zero-based).
724      * @param paint the paint (<code>null</code> permitted).
725      */

726     public void setSectionPaint(int section, Paint JavaDoc paint) {
727         this.sectionPaintList.setPaint(section, paint);
728         notifyListeners(new PlotChangeEvent(this));
729     }
730     
731     /**
732      * Returns the base section paint. This is used when no other paint is
733      * available.
734      *
735      * @return The paint (never <code>null</code>).
736      */

737     public Paint JavaDoc getBaseSectionPaint() {
738         return this.baseSectionPaint;
739     }
740     
741     /**
742      * Sets the base section paint.
743      *
744      * @param paint the paint (<code>null</code> not permitted).
745      */

746     public void setBaseSectionPaint(Paint JavaDoc paint) {
747         if (paint == null) {
748             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
749         }
750         this.baseSectionPaint = paint;
751         notifyListeners(new PlotChangeEvent(this));
752     }
753     
754     //// SECTION OUTLINE PAINT ////////////////////////////////////////////////
755

756     /**
757      * Returns the outline paint for ALL sections in the plot.
758      *
759      * @return The paint (possibly <code>null</code>).
760      */

761     public Paint JavaDoc getSectionOutlinePaint() {
762         return this.sectionOutlinePaint;
763     }
764
765     /**
766      * Sets the outline paint for ALL sections in the plot. If this is set to
767      * </code>null</code>, then a list of paints is used instead (to allow
768      * different colors to be used for each section).
769      *
770      * @param paint the paint (<code>null</code> permitted).
771      */

772     public void setSectionOutlinePaint(Paint JavaDoc paint) {
773         this.sectionOutlinePaint = paint;
774         notifyListeners(new PlotChangeEvent(this));
775     }
776
777     /**
778      * Returns the paint for the specified section.
779      *
780      * @param section the section index (zero-based).
781      *
782      * @return The paint (never <code>null</code>).
783      */

784     public Paint JavaDoc getSectionOutlinePaint(int section) {
785         
786         // return the override, if there is one...
787
if (this.sectionOutlinePaint != null) {
788             return this.sectionOutlinePaint;
789         }
790
791         // otherwise look up the paint list
792
Paint JavaDoc result = this.sectionOutlinePaintList.getPaint(section);
793         if (result == null) {
794             result = this.baseSectionOutlinePaint;
795         }
796         return result;
797        
798     }
799     
800     /**
801      * Sets the paint used to fill a section of the pie and sends a
802      * {@link PlotChangeEvent} to all registered listeners.
803      *
804      * @param section the section index (zero-based).
805      * @param paint the paint (<code>null</code> permitted).
806      */

807     public void setSectionOutlinePaint(int section, Paint JavaDoc paint) {
808         this.sectionOutlinePaintList.setPaint(section, paint);
809         notifyListeners(new PlotChangeEvent(this));
810     }
811     
812     /**
813      * Returns the base section paint. This is used when no other paint is
814      * available.
815      *
816      * @return The paint (never <code>null</code>).
817      */

818     public Paint JavaDoc getBaseSectionOutlinePaint() {
819         return this.baseSectionOutlinePaint;
820     }
821     
822     /**
823      * Sets the base section paint.
824      *
825      * @param paint the paint (<code>null</code> not permitted).
826      */

827     public void setBaseSectionOutlinePaint(Paint JavaDoc paint) {
828         if (paint == null) {
829             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
830         }
831         this.baseSectionOutlinePaint = paint;
832         notifyListeners(new PlotChangeEvent(this));
833     }
834     
835     //// SECTION OUTLINE STROKE ///////////////////////////////////////////////
836

837     /**
838      * Returns the outline stroke for ALL sections in the plot.
839      *
840      * @return The stroke (possibly <code>null</code>).
841      */

842     public Stroke JavaDoc getSectionOutlineStroke() {
843         return this.sectionOutlineStroke;
844     }
845
846     /**
847      * Sets the outline stroke for ALL sections in the plot. If this is set to
848      * </code>null</code>, then a list of paints is used instead (to allow
849      * different colors to be used for each section).
850      *
851      * @param stroke the stroke (<code>null</code> permitted).
852      */

853     public void setSectionOutlineStroke(Stroke JavaDoc stroke) {
854         this.sectionOutlineStroke = stroke;
855         notifyListeners(new PlotChangeEvent(this));
856     }
857
858     /**
859      * Returns the stroke for the specified section.
860      *
861      * @param section the section index (zero-based).
862      *
863      * @return The stroke (never <code>null</code>).
864      */

865     public Stroke JavaDoc getSectionOutlineStroke(int section) {
866         
867         // return the override, if there is one...
868
if (this.sectionOutlineStroke != null) {
869             return this.sectionOutlineStroke;
870         }
871
872         // otherwise look up the paint list
873
Stroke JavaDoc result = this.sectionOutlineStrokeList.getStroke(section);
874         if (result == null) {
875             result = this.baseSectionOutlineStroke;
876         }
877         return result;
878        
879     }
880     
881     /**
882      * Sets the stroke used to fill a section of the pie and sends a
883      * {@link PlotChangeEvent} to all registered listeners.
884      *
885      * @param section the section index (zero-based).
886      * @param stroke the stroke (<code>null</code> permitted).
887      */

888     public void setSectionOutlineStroke(int section, Stroke JavaDoc stroke) {
889         this.sectionOutlineStrokeList.setStroke(section, stroke);
890         notifyListeners(new PlotChangeEvent(this));
891     }
892     
893     /**
894      * Returns the base section stroke. This is used when no other stroke is
895      * available.
896      *
897      * @return The stroke (never <code>null</code>).
898      */

899     public Stroke JavaDoc getBaseSectionOutlineStroke() {
900         return this.baseSectionOutlineStroke;
901     }
902     
903     /**
904      * Sets the base section stroke.
905      *
906      * @param stroke the stroke (<code>null</code> not permitted).
907      */

908     public void setBaseSectionOutlineStroke(Stroke JavaDoc stroke) {
909         if (stroke == null) {
910             throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
911         }
912         this.baseSectionOutlineStroke = stroke;
913         notifyListeners(new PlotChangeEvent(this));
914     }
915
916     /**
917      * Returns the shadow paint.
918      *
919      * @return The paint (possibly <code>null</code>).
920      */

921     public Paint JavaDoc getShadowPaint() {
922         return this.shadowPaint;
923     }
924     
925     /**
926      * Sets the shadow paint and sends a {@link PlotChangeEvent} to all
927      * registered listeners.
928      *
929      * @param paint the paint (<code>null</code> permitted).
930      */

931     public void setShadowPaint(Paint JavaDoc paint) {
932         this.shadowPaint = paint;
933         notifyListeners(new PlotChangeEvent(this));
934     }
935     
936     /**
937      * Returns the x-offset for the shadow effect.
938      *
939      * @return The offset (in Java2D units).
940      */

941     public double getShadowXOffset() {
942         return this.shadowXOffset;
943     }
944     
945     /**
946      * Sets the x-offset for the shadow effect and sends a
947      * {@link PlotChangeEvent} to all registered listeners.
948      *
949      * @param offset the offset (in Java2D units).
950      */

951     public void setShadowXOffset(double offset) {
952         this.shadowXOffset = offset;
953         notifyListeners(new PlotChangeEvent(this));
954     }
955     
956     /**
957      * Returns the y-offset for the shadow effect.
958      *
959      * @return The offset (in Java2D units).
960      */

961     public double getShadowYOffset() {
962         return this.shadowYOffset;
963     }
964     
965     /**
966      * Sets the y-offset for the shadow effect and sends a
967      * {@link PlotChangeEvent} to all registered listeners.
968      *
969      * @param offset the offset (in Java2D units).
970      */

971     public void setShadowYOffset(double offset) {
972         this.shadowYOffset = offset;
973         notifyListeners(new PlotChangeEvent(this));
974     }
975     
976     /**
977      * Returns the amount that a section should be 'exploded'.
978      *
979      * @param section the section number.
980      *
981      * @return The amount that a section should be 'exploded'.
982      */

983     public double getExplodePercent(int section) {
984         double result = 0.0;
985         if (this.explodePercentages != null) {
986             Number JavaDoc percent = (Number JavaDoc) this.explodePercentages.get(section);
987             if (percent != null) {
988                 result = percent.doubleValue();
989             }
990         }
991         return result;
992     }
993
994     /**
995      * Sets the amount that a pie section should be exploded and sends a
996      * {@link PlotChangeEvent} to all registered listeners.
997      *
998      * @param section the section index.
999      * @param percent the explode percentage (0.30 = 30 percent).
1000     */

1001    public void setExplodePercent(int section, double percent) {
1002        if (this.explodePercentages == null) {
1003            this.explodePercentages = new ObjectList();
1004        }
1005        this.explodePercentages.set(section, new Double JavaDoc(percent));
1006        notifyListeners(new PlotChangeEvent(this));
1007    }
1008    
1009    /**
1010     * Returns the maximum explode percent.
1011     *
1012     * @return The percent.
1013     */

1014    public double getMaximumExplodePercent() {
1015        double result = 0.0;
1016        for (int i = 0; i < this.explodePercentages.size(); i++) {
1017            Number JavaDoc explode = (Number JavaDoc) this.explodePercentages.get(i);
1018            if (explode != null) {
1019                result = Math.max(result, explode.doubleValue());
1020            }
1021        }
1022        return result;
1023    }
1024    
1025    /**
1026     * Returns the section label generator.
1027     *
1028     * @return The generator (possibly <code>null</code>).
1029     */

1030    public PieSectionLabelGenerator getLabelGenerator() {
1031        return this.labelGenerator;
1032    }
1033    
1034    /**
1035     * Sets the section label generator and sends a {@link PlotChangeEvent} to
1036     * all registered listeners.
1037     *
1038     * @param generator the generator (<code>null</code> permitted).
1039     */

1040    public void setLabelGenerator(PieSectionLabelGenerator generator) {
1041        this.labelGenerator = generator;
1042        notifyListeners(new PlotChangeEvent(this));
1043    }
1044    
1045    /**
1046     * Returns the gap between the edge of the pie and the labels, expressed as
1047     * a percentage of the plot width.
1048     *
1049     * @return The gap (a percentage, where 0.05 = five percent).
1050     */

1051    public double getLabelGap() {
1052        return this.labelGap;
1053    }
1054    
1055    /**
1056     * Sets the gap between the edge of the pie and the labels (expressed as a
1057     * percentage of the plot width) and sends a {@link PlotChangeEvent} to all
1058     * registered listeners.
1059     *
1060     * @param gap the gap (a percentage, where 0.05 = five percent).
1061     */

1062    public void setLabelGap(double gap) {
1063        this.labelGap = gap;
1064        notifyListeners(new PlotChangeEvent(this));
1065    }
1066    
1067    /**
1068     * Returns the maximum label width as a percentage of the plot width.
1069     *
1070     * @return The width (a percentage, where 0.20 = 20 percent).
1071     */

1072    public double getMaximumLabelWidth() {
1073        return this.maximumLabelWidth;
1074    }
1075    
1076    /**
1077     * Sets the maximum label width as a percentage of the plot width and sends
1078     * a {@link PlotChangeEvent} to all registered listeners.
1079     *
1080     * @param width the width (a percentage, where 0.20 = 20 percent).
1081     */

1082    public void setMaximumLabelWidth(double width) {
1083        this.maximumLabelWidth = width;
1084        notifyListeners(new PlotChangeEvent(this));
1085    }
1086    
1087    /**
1088     * Returns the flag that controls whether or not label linking lines are
1089     * visible.
1090     *
1091     * @return A boolean.
1092     */

1093    public boolean getLabelLinksVisible() {
1094        return this.labelLinksVisible;
1095    }
1096    
1097    /**
1098     * Sets the flag that controls whether or not label linking lines are
1099     * visible and sends a {@link PlotChangeEvent} to all registered listeners.
1100     * Please take care when hiding the linking lines - depending on the data
1101     * values, the labels can be displayed some distance away from the
1102     * corresponding pie section.
1103     *
1104     * @param visible the flag.
1105     */

1106    public void setLabelLinksVisible(boolean visible) {
1107        this.labelLinksVisible = visible;
1108        notifyListeners(new PlotChangeEvent(this));
1109    }
1110    
1111    /**
1112     * Returns the margin (expressed as a percentage of the width or height)
1113     * between the edge of the pie and the link point.
1114     *
1115     * @return The link margin (as a percentage, where 0.05 is five percent).
1116     */

1117    public double getLabelLinkMargin() {
1118        return this.labelLinkMargin;
1119    }
1120    
1121    /**
1122     * Sets the link margin and sends a {@link PlotChangeEvent} to all
1123     * registered listeners.
1124     *
1125     * @param margin the margin.
1126     */

1127    public void setLabelLinkMargin(double margin) {
1128        this.labelLinkMargin = margin;
1129        notifyListeners(new PlotChangeEvent(this));
1130    }
1131    
1132    /**
1133     * Returns the paint used for the lines that connect pie sections to their
1134     * corresponding labels.
1135     *
1136     * @return The paint (never <code>null</code>).
1137     */

1138    public Paint JavaDoc getLabelLinkPaint() {
1139        return this.labelLinkPaint;
1140    }
1141    
1142    /**
1143     * Sets the paint used for the lines that connect pie sections to their
1144     * corresponding labels, and sends a {@link PlotChangeEvent} to all
1145     * registered listeners.
1146     *
1147     * @param paint the paint (<code>null</code> not permitted).
1148     */

1149    public void setLabelLinkPaint(Paint JavaDoc paint) {
1150        if (paint == null) {
1151            throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
1152        }
1153        this.labelLinkPaint = paint;
1154        notifyListeners(new PlotChangeEvent(this));
1155    }
1156    
1157    /**
1158     * Returns the stroke used for the label linking lines.
1159     *
1160     * @return The stroke.
1161     */

1162    public Stroke JavaDoc getLabelLinkStroke() {
1163        return this.labelLinkStroke;
1164    }
1165    
1166    /**
1167     * Sets the link stroke and sends a {@link PlotChangeEvent} to all
1168     * registered listeners.
1169     *
1170     * @param stroke the stroke.
1171     */

1172    public void setLabelLinkStroke(Stroke JavaDoc stroke) {
1173        if (stroke == null) {
1174            throw new IllegalArgumentException JavaDoc("Null 'stroke' argument.");
1175        }
1176        this.labelLinkStroke = stroke;
1177        notifyListeners(new PlotChangeEvent(this));
1178    }
1179    
1180    /**
1181     * Returns the section label font.
1182     *
1183     * @return The font (never <code>null</code>).
1184     */

1185    public Font JavaDoc getLabelFont() {
1186        return this.labelFont;
1187    }
1188
1189    /**
1190     * Sets the section label font and sends a {@link PlotChangeEvent} to all
1191     * registered listeners.
1192     *
1193     * @param font the font (<code>null</code> not permitted).
1194     */

1195    public void setLabelFont(Font JavaDoc font) {
1196        if (font == null) {
1197            throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
1198        }
1199        this.labelFont = font;
1200        notifyListeners(new PlotChangeEvent(this));
1201    }
1202
1203    /**
1204     * Returns the section label paint.
1205     *
1206     * @return The paint (never <code>null</code>).
1207     */

1208    public Paint JavaDoc getLabelPaint() {
1209        return this.labelPaint;
1210    }
1211
1212    /**
1213     * Sets the section label paint and sends a {@link PlotChangeEvent} to all
1214     * registered listeners.
1215     *
1216     * @param paint the paint (<code>null</code> not permitted).
1217     */

1218    public void setLabelPaint(Paint JavaDoc paint) {
1219        if (paint == null) {
1220            throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
1221        }
1222        this.labelPaint = paint;
1223        notifyListeners(new PlotChangeEvent(this));
1224    }
1225
1226    /**
1227     * Returns the section label background paint.
1228     *
1229     * @return The paint (possibly <code>null</code>).
1230     */

1231    public Paint JavaDoc getLabelBackgroundPaint() {
1232        return this.labelBackgroundPaint;
1233    }
1234
1235    /**
1236     * Sets the section label background paint and sends a
1237     * {@link PlotChangeEvent} to all registered listeners.
1238     *
1239     * @param paint the paint (<code>null</code> permitted).
1240     */

1241    public void setLabelBackgroundPaint(Paint JavaDoc paint) {
1242        this.labelBackgroundPaint = paint;
1243        notifyListeners(new PlotChangeEvent(this));
1244    }
1245
1246    /**
1247     * Returns the section label outline paint.
1248     *
1249     * @return The paint (possibly <code>null</code>).
1250     */

1251    public Paint JavaDoc getLabelOutlinePaint() {
1252        return this.labelOutlinePaint;
1253    }
1254
1255    /**
1256     * Sets the section label outline paint and sends a
1257     * {@link PlotChangeEvent} to all registered listeners.
1258     *
1259     * @param paint the paint (<code>null</code> permitted).
1260     */

1261    public void setLabelOutlinePaint(Paint JavaDoc paint) {
1262        this.labelOutlinePaint = paint;
1263        notifyListeners(new PlotChangeEvent(this));
1264    }
1265
1266    /**
1267     * Returns the section label outline stroke.
1268     *
1269     * @return The stroke (possibly <code>null</code>).
1270     */

1271    public Stroke JavaDoc getLabelOutlineStroke() {
1272        return this.labelOutlineStroke;
1273    }
1274
1275    /**
1276     * Sets the section label outline stroke and sends a
1277     * {@link PlotChangeEvent} to all registered listeners.
1278     *
1279     * @param stroke the stroke (<code>null</code> permitted).
1280     */

1281    public void setLabelOutlineStroke(Stroke JavaDoc stroke) {
1282        this.labelOutlineStroke = stroke;
1283        notifyListeners(new PlotChangeEvent(this));
1284    }
1285
1286    /**
1287     * Returns the section label shadow paint.
1288     *
1289     * @return The paint (possibly <code>null</code>).
1290     */

1291    public Paint JavaDoc getLabelShadowPaint() {
1292        return this.labelShadowPaint;
1293    }
1294
1295    /**
1296     * Sets the section label shadow paint and sends a {@link PlotChangeEvent}
1297     * to all registered listeners.
1298     *
1299     * @param paint the paint (<code>null</code> permitted).
1300     */

1301    public void setLabelShadowPaint(Paint JavaDoc paint) {
1302        this.labelShadowPaint = paint;
1303        notifyListeners(new PlotChangeEvent(this));
1304    }
1305
1306    /**
1307     * Returns the tool tip generator, an object that is responsible for
1308     * generating the text items used for tool tips by the plot. If the
1309     * generator is <code>null</code>, no tool tips will be created.
1310     *
1311     * @return The generator (possibly <code>null</code>).
1312     */

1313    public PieToolTipGenerator getToolTipGenerator() {
1314        return this.toolTipGenerator;
1315    }
1316
1317    /**
1318     * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all
1319     * registered listeners. Set the generator to <code>null</code> if you
1320     * don't want any tool tips.
1321     *
1322     * @param generator the generator (<code>null</code> permitted).
1323     */

1324    public void setToolTipGenerator(PieToolTipGenerator generator) {
1325        this.toolTipGenerator = generator;
1326        notifyListeners(new PlotChangeEvent(this));
1327    }
1328
1329    /**
1330     * Returns the URL generator.
1331     *
1332     * @return The generator (possibly <code>null</code>).
1333     */

1334    public PieURLGenerator getURLGenerator() {
1335        return this.urlGenerator;
1336    }
1337
1338    /**
1339     * Sets the URL generator and sends a {@link PlotChangeEvent} to all
1340     * registered listeners.
1341     *
1342     * @param generator the generator (<code>null</code> permitted).
1343     */

1344    public void setURLGenerator(PieURLGenerator generator) {
1345        this.urlGenerator = generator;
1346        notifyListeners(new PlotChangeEvent(this));
1347    }
1348
1349    /**
1350     * Returns the minimum arc angle that will be drawn. Pie sections for an
1351     * angle smaller than this are not drawn, to avoid a JDK bug.
1352     *
1353     * @return The minimum angle.
1354     */

1355    public double getMinimumArcAngleToDraw() {
1356        return this.minimumArcAngleToDraw;
1357    }
1358
1359    /**
1360     * Sets the minimum arc angle that will be drawn. Pie sections for an
1361     * angle smaller than this are not drawn, to avoid a JDK bug. See this
1362     * link for details:
1363     * <br><br>
1364     * <a HREF="http://www.jfree.org/phpBB2/viewtopic.php?t=2707">
1365     * http://www.jfree.org/phpBB2/viewtopic.php?t=2707</a>
1366     * <br><br>
1367     * ...and this bug report in the Java Bug Parade:
1368     * <br><br>
1369     * <a HREF=
1370     * "http://developer.java.sun.com/developer/bugParade/bugs/4836495.html">
1371     * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html</a>
1372     *
1373     * @param angle the minimum angle.
1374     */

1375    public void setMinimumArcAngleToDraw(double angle) {
1376        this.minimumArcAngleToDraw = angle;
1377    }
1378    
1379    /**
1380     * Returns the shape used for legend items.
1381     *
1382     * @return The shape.
1383     */

1384    public Shape JavaDoc getLegendItemShape() {
1385        return this.legendItemShape;
1386    }
1387
1388    /**
1389     * Sets the shape used for legend items.
1390     *
1391     * @param shape the shape (<code>null</code> not permitted).
1392     */

1393    public void setLegendItemShape(Shape JavaDoc shape) {
1394        if (shape == null) {
1395            throw new IllegalArgumentException JavaDoc("Null 'shape' argument.");
1396        }
1397        this.legendItemShape = shape;
1398        notifyListeners(new PlotChangeEvent(this));
1399    }
1400    
1401    /**
1402     * Returns the legend label tool tip generator.
1403     *
1404     * @return The legend label tool tip generator (possibly <code>null</code>).
1405     */

1406    public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
1407        return this.legendLabelToolTipGenerator;
1408    }
1409    
1410    /**
1411     * Sets the legend label tool tip generator and sends a
1412     * {@link PlotChangeEvent} to all registered listeners.
1413     *
1414     * @param generator the generator (<code>null</code> permitted).
1415     */

1416    public void setLegendLabelToolTipGenerator(
1417            PieSectionLabelGenerator generator) {
1418        this.legendLabelToolTipGenerator = generator;
1419        notifyListeners(new PlotChangeEvent(this));
1420    }
1421    
1422    /**
1423     * Returns the legend label generator.
1424     *
1425     * @return The legend label generator (never <code>null</code>).
1426     */

1427    public PieSectionLabelGenerator getLegendLabelGenerator() {
1428        return this.legendLabelGenerator;
1429    }
1430    
1431    /**
1432     * Sets the legend label generator and sends a {@link PlotChangeEvent} to
1433     * all registered listeners.
1434     *
1435     * @param generator the generator (<code>null</code> not permitted).
1436     */

1437    public void setLegendLabelGenerator(PieSectionLabelGenerator generator) {
1438        if (generator == null) {
1439            throw new IllegalArgumentException JavaDoc("Null 'generator' argument.");
1440        }
1441        this.legendLabelGenerator = generator;
1442        notifyListeners(new PlotChangeEvent(this));
1443    }
1444    
1445    /**
1446     * Initialises the drawing procedure. This method will be called before
1447     * the first item is rendered, giving the plot an opportunity to initialise
1448     * any state information it wants to maintain.
1449     *
1450     * @param g2 the graphics device.
1451     * @param plotArea the plot area (<code>null</code> not permitted).
1452     * @param plot the plot.
1453     * @param index the secondary index (<code>null</code> for primary
1454     * renderer).
1455     * @param info collects chart rendering information for return to caller.
1456     *
1457     * @return A state object (maintains state information relevant to one
1458     * chart drawing).
1459     */

1460    public PiePlotState initialise(Graphics2D JavaDoc g2,
1461                                   Rectangle2D JavaDoc plotArea,
1462                                   PiePlot plot,
1463                                   Integer JavaDoc index,
1464                                   PlotRenderingInfo info) {
1465     
1466        PiePlotState state = new PiePlotState(info);
1467        state.setPassesRequired(2);
1468        state.setTotal(
1469            DatasetUtilities.calculatePieDatasetTotal(plot.getDataset())
1470        );
1471        state.setLatestAngle(plot.getStartAngle());
1472        return state;
1473        
1474    }
1475    
1476    /**
1477     * Draws the plot on a Java 2D graphics device (such as the screen or a
1478     * printer).
1479     *
1480     * @param g2 the graphics device.
1481     * @param area the area within which the plot should be drawn.
1482     * @param anchor the anchor point (<code>null</code> permitted).
1483     * @param parentState the state from the parent plot, if there is one.
1484     * @param info collects info about the drawing
1485     * (<code>null</code> permitted).
1486     */

1487    public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area, Point2D JavaDoc anchor,
1488                     PlotState parentState,
1489                     PlotRenderingInfo info) {
1490
1491        // adjust for insets...
1492
RectangleInsets insets = getInsets();
1493        insets.trim(area);
1494
1495        if (info != null) {
1496            info.setPlotArea(area);
1497            info.setDataArea(area);
1498        }
1499
1500        drawBackground(g2, area);
1501        drawOutline(g2, area);
1502
1503        Shape JavaDoc savedClip = g2.getClip();
1504        g2.clip(area);
1505
1506        Composite JavaDoc originalComposite = g2.getComposite();
1507        g2.setComposite(
1508            AlphaComposite.getInstance(
1509                AlphaComposite.SRC_OVER, getForegroundAlpha()
1510            )
1511        );
1512
1513        if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
1514            drawPie(g2, area, info);
1515        }
1516        else {
1517            drawNoDataMessage(g2, area);
1518        }
1519
1520        g2.setClip(savedClip);
1521        g2.setComposite(originalComposite);
1522
1523        drawOutline(g2, area);
1524
1525    }
1526
1527    /**
1528     * Draws the pie.
1529     *
1530     * @param g2 the graphics device.
1531     * @param plotArea the plot area.
1532     * @param info chart rendering info.
1533     */

1534    protected void drawPie(Graphics2D JavaDoc g2,
1535                           Rectangle2D JavaDoc plotArea,
1536                           PlotRenderingInfo info) {
1537
1538        PiePlotState state = initialise(g2, plotArea, this, null, info);
1539
1540        // adjust the plot area for interior spacing and labels...
1541
double labelWidth = 0.0;
1542        if (this.labelGenerator != null) {
1543            labelWidth = this.labelGap + this.maximumLabelWidth
1544                         + this.labelLinkMargin;
1545        }
1546        double gapHorizontal
1547            = plotArea.getWidth() * (this.interiorGap + labelWidth);
1548        double gapVertical = plotArea.getHeight() * this.interiorGap;
1549
1550        double linkX = plotArea.getX() + gapHorizontal / 2;
1551        double linkY = plotArea.getY() + gapVertical / 2;
1552        double linkW = plotArea.getWidth() - gapHorizontal;
1553        double linkH = plotArea.getHeight() - gapVertical;
1554        
1555        // make the link area a square if the pie chart is to be circular...
1556
if (this.circular) {
1557            double min = Math.min(linkW, linkH) / 2;
1558            linkX = (linkX + linkX + linkW) / 2 - min;
1559            linkY = (linkY + linkY + linkH) / 2 - min;
1560            linkW = 2 * min;
1561            linkH = 2 * min;
1562        }
1563
1564        // the link area defines the dog leg points for the linking lines to
1565
// the labels
1566
Rectangle2D JavaDoc linkArea = new Rectangle2D.Double JavaDoc(
1567            linkX, linkY, linkW, linkH
1568        );
1569        state.setLinkArea(linkArea);
1570        
1571        // the explode area defines the max circle/ellipse for the exploded
1572
// pie sections. it is defined by shrinking the linkArea by the
1573
// linkMargin factor.
1574
double hh = linkArea.getWidth() * this.labelLinkMargin;
1575        double vv = linkArea.getHeight() * this.labelLinkMargin;
1576        Rectangle2D JavaDoc explodeArea = new Rectangle2D.Double JavaDoc(
1577            linkX + hh / 2.0, linkY + vv / 2.0, linkW - hh, linkH - vv
1578        );
1579       
1580        state.setExplodedPieArea(explodeArea);
1581        
1582        // the pie area defines the circle/ellipse for regular pie sections.
1583
// it is defined by shrinking the explodeArea by the explodeMargin
1584
// factor.
1585
double maximumExplodePercent = getMaximumExplodePercent();
1586        double percent = maximumExplodePercent / (1.0 + maximumExplodePercent);
1587        
1588        double h1 = explodeArea.getWidth() * percent;
1589        double v1 = explodeArea.getHeight() * percent;
1590        Rectangle2D JavaDoc pieArea = new Rectangle2D.Double JavaDoc(
1591            explodeArea.getX() + h1 / 2.0, explodeArea.getY() + v1 / 2.0,
1592            explodeArea.getWidth() - h1, explodeArea.getHeight() - v1
1593        );
1594
1595        state.setPieArea(pieArea);
1596        state.setPieCenterX(pieArea.getCenterX());
1597        state.setPieCenterY(pieArea.getCenterY());
1598        state.setPieWRadius(pieArea.getWidth() / 2.0);
1599        state.setPieHRadius(pieArea.getHeight() / 2.0);
1600        // plot the data (unless the dataset is null)...
1601
if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) {
1602
1603            List JavaDoc keys = this.dataset.getKeys();
1604            double totalValue
1605                = DatasetUtilities.calculatePieDatasetTotal(this.dataset);
1606
1607            int passesRequired = state.getPassesRequired();
1608            for (int pass = 0; pass < passesRequired; pass++) {
1609                double runningTotal = 0.0;
1610                for (int section = 0; section < keys.size(); section++) {
1611                    Number JavaDoc n = this.dataset.getValue(section);
1612                    if (n != null) {
1613                        double value = n.doubleValue();
1614                        if (value > 0.0) {
1615                            runningTotal += value;
1616                            drawItem(g2, section, explodeArea, state, pass);
1617                        }
1618                    }
1619                }
1620            }
1621            
1622            drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
1623
1624        }
1625        else {
1626            drawNoDataMessage(g2, plotArea);
1627        }
1628    }
1629    
1630    /**
1631     * Draws a single data item.
1632     *
1633     * @param g2 the graphics device (<code>null</code> not permitted).
1634     * @param section the section index.
1635     * @param dataArea the data plot area.
1636     * @param state state information for one chart.
1637     * @param currentPass the current pass index.
1638     */

1639    protected void drawItem(Graphics2D JavaDoc g2,
1640                            int section,
1641                            Rectangle2D JavaDoc dataArea,
1642                            PiePlotState state,
1643                            int currentPass) {
1644    
1645        Number JavaDoc n = this.dataset.getValue(section);
1646        if (n == null) {
1647            return;
1648        }
1649        double value = n.doubleValue();
1650        double angle1 = 0.0;
1651        double angle2 = 0.0;
1652        
1653        if (this.direction == Rotation.CLOCKWISE) {
1654            angle1 = state.getLatestAngle();
1655            angle2 = angle1 - value / state.getTotal() * 360.0;
1656        }
1657        else if (this.direction == Rotation.ANTICLOCKWISE) {
1658            angle1 = state.getLatestAngle();
1659            angle2 = angle1 + value / state.getTotal() * 360.0;
1660        }
1661        else {
1662            throw new IllegalStateException JavaDoc("Rotation type not recognised.");
1663        }
1664        
1665        double angle = (angle2 - angle1);
1666        if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
1667            double ep = 0.0;
1668            double mep = getMaximumExplodePercent();
1669            if (mep > 0.0) {
1670                ep = getExplodePercent(section) / mep;
1671            }
1672            Rectangle2D JavaDoc arcBounds = getArcBounds(
1673                state.getPieArea(), state.getExplodedPieArea(),
1674                angle1, angle, ep
1675            );
1676            Arc2D.Double JavaDoc arc = new Arc2D.Double JavaDoc(
1677                arcBounds, angle1, angle, Arc2D.PIE
1678            );
1679            
1680            if (currentPass == 0) {
1681                if (this.shadowPaint != null) {
1682                    Shape JavaDoc shadowArc = ShapeUtilities.createTranslatedShape(
1683                        arc, (float) this.shadowXOffset,
1684                        (float) this.shadowYOffset
1685                    );
1686                    g2.setPaint(this.shadowPaint);
1687                    g2.fill(shadowArc);
1688                }
1689            }
1690            else if (currentPass == 1) {
1691
1692                Paint JavaDoc paint = getSectionPaint(section);
1693                g2.setPaint(paint);
1694                g2.fill(arc);
1695
1696                Paint JavaDoc outlinePaint = getSectionOutlinePaint(section);
1697                Stroke JavaDoc outlineStroke = getSectionOutlineStroke(section);
1698                if (outlinePaint != null && outlineStroke != null) {
1699                    g2.setPaint(outlinePaint);
1700                    g2.setStroke(outlineStroke);
1701                    g2.draw(arc);
1702                }
1703                
1704                // update the linking line target for later
1705
// add an entity for the pie section
1706
if (state.getInfo() != null) {
1707                    EntityCollection entities
1708                        = state.getInfo().getOwner().getEntityCollection();
1709                    if (entities != null) {
1710                        Comparable JavaDoc key = this.dataset.getKey(section);
1711                        String JavaDoc tip = null;
1712                        if (this.toolTipGenerator != null) {
1713                            tip = this.toolTipGenerator.generateToolTip(
1714                                this.dataset, key
1715                            );
1716                        }
1717                        String JavaDoc url = null;
1718                        if (this.urlGenerator != null) {
1719                            url = this.urlGenerator.generateURL(
1720                                this.dataset, key, this.pieIndex
1721                            );
1722                        }
1723                        PieSectionEntity entity = new PieSectionEntity(
1724                            arc, this.dataset, this.pieIndex, section, key,
1725                            tip, url
1726                        );
1727                        entities.add(entity);
1728                    }
1729                }
1730            }
1731        }
1732        state.setLatestAngle(angle2);
1733    }
1734    
1735    /**
1736     * Draws the labels for the pie sections.
1737     *
1738     * @param g2 the graphics device.
1739     * @param keys the keys.
1740     * @param totalValue the total value.
1741     * @param plotArea the plot area.
1742     * @param linkArea the link area.
1743     * @param state the state.
1744     */

1745    protected void drawLabels(Graphics2D JavaDoc g2, List JavaDoc keys, double totalValue,
1746                              Rectangle2D JavaDoc plotArea, Rectangle2D JavaDoc linkArea,
1747                              PiePlotState state) {
1748
1749        Composite JavaDoc originalComposite = g2.getComposite();
1750        g2.setComposite(
1751            AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)
1752        );
1753
1754        // classify the keys according to which side the label will appear...
1755
DefaultKeyedValues leftKeys = new DefaultKeyedValues();
1756        DefaultKeyedValues rightKeys = new DefaultKeyedValues();
1757       
1758        double runningTotal1 = 0.0;
1759        Iterator JavaDoc iterator1 = keys.iterator();
1760        while (iterator1.hasNext()) {
1761            Comparable JavaDoc key = (Comparable JavaDoc) iterator1.next();
1762            Number JavaDoc n = this.dataset.getValue(key);
1763            if (n != null) {
1764                double v = n.doubleValue();
1765                if (this.ignoreZeroValues ? v > 0.0 : v >= 0.0) {
1766                    runningTotal1 = runningTotal1 + v;
1767                    // work out the mid angle (0 - 90 and 270 - 360) = right,
1768
// otherwise left
1769
double mid = this.startAngle + (this.direction.getFactor()
1770                        * ((runningTotal1 - v / 2.0) * 360) / totalValue);
1771                    if (Math.cos(Math.toRadians(mid)) < 0.0) {
1772                        leftKeys.addValue(key, new Double JavaDoc(mid));
1773                    }
1774                    else {
1775                        rightKeys.addValue(key, new Double JavaDoc(mid));
1776                    }
1777                }
1778            }
1779        }
1780       
1781        g2.setFont(getLabelFont());
1782        float maxLabelWidth
1783            = (float) (getMaximumLabelWidth() * plotArea.getWidth());
1784        
1785        // draw the left labels...
1786
if (this.labelGenerator != null) {
1787            drawLeftLabels(
1788                leftKeys, g2, plotArea, linkArea, maxLabelWidth, state
1789            );
1790            drawRightLabels(
1791                rightKeys, g2, plotArea, linkArea, maxLabelWidth, state
1792            );
1793        }
1794        g2.setComposite(originalComposite);
1795
1796    }
1797
1798    /**
1799     * Draws the left labels.
1800     *
1801     * @param leftKeys the keys.
1802     * @param g2 the graphics device.
1803     * @param plotArea the plot area.
1804     * @param linkArea the link area.
1805     * @param maxLabelWidth the maximum label width.
1806     * @param state the state.
1807     */

1808    protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D JavaDoc g2,
1809                                  Rectangle2D JavaDoc plotArea, Rectangle2D JavaDoc linkArea,
1810                                  float maxLabelWidth, PiePlotState state) {
1811        
1812        PieLabelDistributor distributor1 = new PieLabelDistributor(
1813            leftKeys.getItemCount()
1814        );
1815        double lGap = plotArea.getWidth() * this.labelGap;
1816        double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
1817        for (int i = 0; i < leftKeys.getItemCount(); i++) {
1818            String JavaDoc label = this.labelGenerator.generateSectionLabel(
1819                this.dataset, leftKeys.getKey(i)
1820            );
1821            if (label != null) {
1822                TextBlock block = TextUtilities.createTextBlock(
1823                    label,
1824                    this.labelFont, this.labelPaint, maxLabelWidth,
1825                    new G2TextMeasurer(g2)
1826                );
1827                TextBox labelBox = new TextBox(block);
1828                labelBox.setBackgroundPaint(this.labelBackgroundPaint);
1829                labelBox.setOutlinePaint(this.labelOutlinePaint);
1830                labelBox.setOutlineStroke(this.labelOutlineStroke);
1831                labelBox.setShadowPaint(this.labelShadowPaint);
1832                double theta = Math.toRadians(
1833                    leftKeys.getValue(i).doubleValue()
1834                );
1835                double baseY = state.getPieCenterY() - Math.sin(theta)
1836                               * verticalLinkRadius;
1837                double hh = labelBox.getHeight(g2);
1838
1839                distributor1.addPieLabelRecord(
1840                    new PieLabelRecord(
1841                        leftKeys.getKey(i), theta, baseY, labelBox, hh,
1842                        lGap / 2.0 + lGap / 2.0 * -Math.cos(theta),
1843                        0.9 + getExplodePercent(this.dataset.getIndex(
1844                                leftKeys.getKey(i)))
1845                    )
1846                );
1847            }
1848        }
1849        distributor1.distributeLabels(plotArea.getMinY(), plotArea.getHeight());
1850        for (int i = 0; i < distributor1.getItemCount(); i++) {
1851            drawLeftLabel(g2, state, distributor1.getPieLabelRecord(i));
1852        }
1853    }
1854    
1855    /**
1856     * Draws the right labels.
1857     *
1858     * @param keys the keys.
1859     * @param g2 the graphics device.
1860     * @param plotArea the plot area.
1861     * @param linkArea the link area.
1862     * @param maxLabelWidth the maximum label width.
1863     * @param state the state.
1864     */

1865    protected void drawRightLabels(KeyedValues keys, Graphics2D JavaDoc g2,
1866                                   Rectangle2D JavaDoc plotArea, Rectangle2D JavaDoc linkArea,
1867                                   float maxLabelWidth, PiePlotState state) {
1868
1869        // draw the right labels...
1870
PieLabelDistributor distributor2
1871            = new PieLabelDistributor(keys.getItemCount());
1872        double lGap = plotArea.getWidth() * this.labelGap;
1873        double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
1874
1875        for (int i = 0; i < keys.getItemCount(); i++) {
1876            String JavaDoc label = this.labelGenerator.generateSectionLabel(
1877                this.dataset, keys.getKey(i)
1878            );
1879
1880            if (label != null) {
1881                TextBlock block = TextUtilities.createTextBlock(
1882                    label, this.labelFont, this.labelPaint,
1883                    maxLabelWidth, new G2TextMeasurer(g2)
1884                );
1885                TextBox labelBox = new TextBox(block);
1886                labelBox.setBackgroundPaint(this.labelBackgroundPaint);
1887                labelBox.setOutlinePaint(this.labelOutlinePaint);
1888                labelBox.setOutlineStroke(this.labelOutlineStroke);
1889                labelBox.setShadowPaint(this.labelShadowPaint);
1890                double theta = Math.toRadians(keys.getValue(i).doubleValue());
1891                double baseY = state.getPieCenterY()
1892                              - Math.sin(theta) * verticalLinkRadius;
1893                double hh = labelBox.getHeight(g2);
1894                distributor2.addPieLabelRecord(
1895                    new PieLabelRecord(
1896                        keys.getKey(i), theta, baseY, labelBox, hh,
1897                        lGap / 2.0 + lGap / 2.0 * Math.cos(theta),
1898                        0.9 + getExplodePercent(this.dataset.getIndex(
1899                                keys.getKey(i)))
1900                    )
1901                );
1902            }
1903        }
1904        distributor2.distributeLabels(linkArea.getMinY(), linkArea.getHeight());
1905        for (int i = 0; i < distributor2.getItemCount(); i++) {
1906            drawRightLabel(g2, state, distributor2.getPieLabelRecord(i));
1907        }
1908
1909    }
1910    
1911    /**
1912     * Returns a collection of legend items for the pie chart.
1913     *
1914     * @return The legend items (never <code>null</code>).
1915     */

1916    public LegendItemCollection getLegendItems() {
1917
1918        LegendItemCollection result = new LegendItemCollection();
1919
1920        List JavaDoc keys = null;
1921        if (this.dataset != null) {
1922            keys = this.dataset.getKeys();
1923            int section = 0;
1924            Shape JavaDoc shape = getLegendItemShape();
1925            Iterator JavaDoc iterator = keys.iterator();
1926            while (iterator.hasNext()) {
1927                Comparable JavaDoc key = (Comparable JavaDoc) iterator.next();
1928                Number JavaDoc n = this.dataset.getValue(key);
1929                if (n != null || !this.ignoreNullValues) {
1930                    String JavaDoc label
1931                        = this.legendLabelGenerator.generateSectionLabel(
1932                            this.dataset, key
1933                        );
1934                    String JavaDoc description = label;
1935                    String JavaDoc toolTipText = null;
1936                    if (this.legendLabelToolTipGenerator != null) {
1937                        toolTipText
1938                        = this.legendLabelToolTipGenerator.generateSectionLabel(
1939                            this.dataset, key
1940                        );
1941                    }
1942                    String JavaDoc urlText = null;
1943                    Paint JavaDoc paint = getSectionPaint(section);
1944                    Paint JavaDoc outlinePaint = getSectionOutlinePaint(section);
1945                    Stroke JavaDoc outlineStroke = getSectionOutlineStroke(section);
1946
1947                    LegendItem item = new LegendItem(
1948                        label, description, toolTipText, urlText,
1949                        true, shape,
1950                        true, paint,
1951                        true, outlinePaint, outlineStroke,
1952                        false, // line not visible
1953
new Line2D.Float JavaDoc(),
1954                        new BasicStroke JavaDoc(),
1955                        Color.black
1956                    );
1957 
1958                    result.add(item);
1959                    section++;
1960                }
1961            }
1962        }
1963
1964        return result;
1965    }
1966
1967    /**
1968     * Returns a short string describing the type of plot.
1969     *
1970     * @return The plot type.
1971     */

1972    public String JavaDoc getPlotType() {
1973        return localizationResources.getString("Pie_Plot");
1974    }
1975
1976    /**
1977     * A zoom method that does nothing.
1978     * <p>
1979     * Plots are required to support the zoom operation. In the case of a pie
1980     * chart, it doesn't make sense to zoom in or out, so the method is empty.
1981     *
1982     * @param percent the zoom percentage.
1983     */

1984    public void zoom(double percent) {
1985        // no zooming for pie plots
1986
}
1987
1988    /**
1989     * Returns a rectangle that can be used to create a pie section (taking
1990     * into account the amount by which the pie section is 'exploded').
1991     *
1992     * @param unexploded the area inside which the unexploded pie sections are
1993     * drawn.
1994     * @param exploded the area inside which the exploded pie sections are
1995     * drawn.
1996     * @param angle the start angle.
1997     * @param extent the extent of the arc.
1998     * @param explodePercent the amount by which the pie section is exploded.
1999     *
2000     * @return A rectangle that can be used to create a pie section.
2001     */

2002    protected Rectangle2D JavaDoc getArcBounds(Rectangle2D JavaDoc unexploded,
2003                                       Rectangle2D JavaDoc exploded,
2004                                       double angle, double extent,
2005                                       double explodePercent) {
2006
2007        if (explodePercent == 0.0) {
2008            return unexploded;
2009        }
2010        else {
2011            Arc2D JavaDoc arc1 = new Arc2D.Double JavaDoc(
2012                unexploded, angle, extent / 2, Arc2D.OPEN
2013            );
2014            Point2D JavaDoc point1 = arc1.getEndPoint();
2015            Arc2D.Double JavaDoc arc2 = new Arc2D.Double JavaDoc(
2016                exploded, angle, extent / 2, Arc2D.OPEN
2017            );
2018            Point2D JavaDoc point2 = arc2.getEndPoint();
2019            double deltaX = (point1.getX() - point2.getX()) * explodePercent;
2020            double deltaY = (point1.getY() - point2.getY()) * explodePercent;
2021            return new Rectangle2D.Double JavaDoc(
2022                unexploded.getX() - deltaX, unexploded.getY() - deltaY,
2023                unexploded.getWidth(), unexploded.getHeight()
2024            );
2025        }
2026    }
2027    
2028    /**
2029     * Draws a section label on the left side of the pie chart.
2030     *
2031     * @param g2 the graphics device.
2032     * @param state the state.
2033     * @param record the label record.
2034     */

2035    protected void drawLeftLabel(Graphics2D JavaDoc g2, PiePlotState state,
2036                                 PieLabelRecord record) {
2037
2038        double anchorX = state.getLinkArea().getMinX();
2039        double targetX = anchorX - record.getGap();
2040        double targetY = record.getAllocatedY();
2041        
2042        if (this.labelLinksVisible) {
2043            double theta = record.getAngle();
2044            double linkX = state.getPieCenterX() + Math.cos(theta)
2045                * state.getPieWRadius() * record.getLinkPercent();
2046            double linkY = state.getPieCenterY() - Math.sin(theta)
2047                * state.getPieHRadius() * record.getLinkPercent();
2048            double elbowX = state.getPieCenterX() + Math.cos(theta)
2049                * state.getLinkArea().getWidth() / 2.0;
2050            double elbowY = state.getPieCenterY() - Math.sin(theta)
2051                * state.getLinkArea().getHeight() / 2.0;
2052            double anchorY = elbowY;
2053            g2.setPaint(this.labelLinkPaint);
2054            g2.setStroke(this.labelLinkStroke);
2055            g2.draw(new Line2D.Double JavaDoc(linkX, linkY, elbowX, elbowY));
2056            g2.draw(new Line2D.Double JavaDoc(anchorX, anchorY, elbowX, elbowY));
2057            g2.draw(new Line2D.Double JavaDoc(anchorX, anchorY, targetX, targetY));
2058        }
2059        TextBox tb = record.getLabel();
2060        tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT);
2061        
2062    }
2063
2064    /**
2065     * Draws a section label on the right side of the pie chart.
2066     *
2067     * @param g2 the graphics device.
2068     * @param state the state.
2069     * @param record the label record.
2070     */

2071    protected void drawRightLabel(Graphics2D JavaDoc g2, PiePlotState state,
2072                                  PieLabelRecord record) {
2073        
2074        double anchorX = state.getLinkArea().getMaxX();
2075        double targetX = anchorX + record.getGap();
2076        double targetY = record.getAllocatedY();
2077        
2078        if (this.labelLinksVisible) {
2079            double theta = record.getAngle();
2080            double linkX = state.getPieCenterX() + Math.cos(theta)
2081                * state.getPieWRadius() * record.getLinkPercent();
2082            double linkY = state.getPieCenterY() - Math.sin(theta)
2083                * state.getPieHRadius() * record.getLinkPercent();
2084            double elbowX = state.getPieCenterX() + Math.cos(theta)
2085                * state.getLinkArea().getWidth() / 2.0;
2086            double elbowY = state.getPieCenterY() - Math.sin(theta)
2087                * state.getLinkArea().getHeight() / 2.0;
2088            double anchorY = elbowY;
2089            g2.setPaint(this.labelLinkPaint);
2090            g2.setStroke(this.labelLinkStroke);
2091            g2.draw(new Line2D.Double JavaDoc(linkX, linkY, elbowX, elbowY));
2092            g2.draw(new Line2D.Double JavaDoc(anchorX, anchorY, elbowX, elbowY));
2093            g2.draw(new Line2D.Double JavaDoc(anchorX, anchorY, targetX, targetY));
2094        }
2095        
2096        TextBox tb = record.getLabel();
2097        tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT);
2098    
2099    }
2100
2101    /**
2102     * Tests this plot for equality with an arbitrary object. Note that the
2103     * plot's dataset is NOT included in the test for equality.
2104     *
2105     * @param obj the object to test against (<code>null</code> permitted).
2106     *
2107     * @return <code>true</code> or <code>false</code>.
2108     */

2109    public boolean equals(Object JavaDoc obj) {
2110
2111        if (obj == this) {
2112            return true;
2113        }
2114        if (!(obj instanceof PiePlot)) {
2115            return false;
2116        }
2117        if (!super.equals(obj)) {
2118            return false;
2119        }
2120        PiePlot that = (PiePlot) obj;
2121        if (this.pieIndex != that.pieIndex) {
2122            return false;
2123        }
2124        if (this.interiorGap != that.interiorGap) {
2125            return false;
2126        }
2127        if (this.circular != that.circular) {
2128            return false;
2129        }
2130        if (this.startAngle != that.startAngle) {
2131            return false;
2132        }
2133        if (this.direction != that.direction) {
2134            return false;
2135        }
2136        if (this.ignoreZeroValues != that.ignoreZeroValues) {
2137            return false;
2138        }
2139        if (this.ignoreNullValues != that.ignoreNullValues) {
2140            return false;
2141        }
2142        if (!ObjectUtilities.equal(this.sectionPaint, that.sectionPaint)) {
2143            return false;
2144        }
2145        if (!ObjectUtilities.equal(this.sectionPaintList,
2146                that.sectionPaintList)) {
2147            return false;
2148        }
2149        if (!ObjectUtilities.equal(this.baseSectionPaint,
2150                that.baseSectionPaint)) {
2151            return false;
2152        }
2153        if (!ObjectUtilities.equal(this.sectionOutlinePaint,
2154                that.sectionOutlinePaint)) {
2155            return false;
2156        }
2157        if (!ObjectUtilities.equal(this.sectionOutlinePaintList,
2158                that.sectionOutlinePaintList)) {
2159            return false;
2160        }
2161        if (!ObjectUtilities.equal(
2162            this.baseSectionOutlinePaint, that.baseSectionOutlinePaint
2163        )) {
2164            return false;
2165        }
2166        if (!ObjectUtilities.equal(this.sectionOutlineStroke,
2167                that.sectionOutlineStroke)) {
2168            return false;
2169        }
2170        if (!ObjectUtilities.equal(
2171            this.sectionOutlineStrokeList, that.sectionOutlineStrokeList
2172        )) {
2173            return false;
2174        }
2175        if (!ObjectUtilities.equal(
2176            this.baseSectionOutlineStroke, that.baseSectionOutlineStroke
2177        )) {
2178            return false;
2179        }
2180          
2181        if (!ObjectUtilities.equal(this.shadowPaint, that.shadowPaint)) {
2182            return false;
2183        }
2184        if (!(this.shadowXOffset == that.shadowXOffset)) {
2185            return false;
2186        }
2187        if (!(this.shadowYOffset == that.shadowYOffset)) {
2188            return false;
2189        }
2190            
2191        if (!ObjectUtilities.equal(this.explodePercentages,
2192                that.explodePercentages)) {
2193            return false;
2194        }
2195        if (!ObjectUtilities.equal(this.labelGenerator,
2196                that.labelGenerator)) {
2197            return false;
2198        }
2199        if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
2200            return false;
2201        }
2202        if (!ObjectUtilities.equal(this.labelPaint, that.labelPaint)) {
2203            return false;
2204        }
2205            
2206        if (!ObjectUtilities.equal(this.labelBackgroundPaint,
2207                that.labelBackgroundPaint)) {
2208            return false;
2209        }
2210        if (!ObjectUtilities.equal(this.labelOutlinePaint,
2211                that.labelOutlinePaint)) {
2212            return false;
2213        }
2214        if (!ObjectUtilities.equal(this.labelOutlineStroke,
2215                that.labelOutlineStroke)) {
2216            return false;
2217        }
2218        if (!ObjectUtilities.equal(this.labelShadowPaint,
2219                that.labelShadowPaint)) {
2220            return false;
2221        }
2222
2223        if (!(this.maximumLabelWidth == that.maximumLabelWidth)) {
2224            return false;
2225        }
2226        if (!(this.labelGap == that.labelGap)) {
2227            return false;
2228        }
2229        if (!(this.labelLinkMargin == that.labelLinkMargin)) {
2230            return false;
2231        }
2232        if (this.labelLinksVisible != that.labelLinksVisible) {
2233            return false;
2234        }
2235        if (!ObjectUtilities.equal(this.labelLinkPaint, that.labelLinkPaint)) {
2236            return false;
2237        }
2238        if (!ObjectUtilities.equal(this.labelLinkStroke,
2239                that.labelLinkStroke)) {
2240            return false;
2241        }
2242        if (!ObjectUtilities.equal(this.toolTipGenerator,
2243                that.toolTipGenerator)) {
2244            return false;
2245        }
2246        if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
2247            return false;
2248        }
2249        if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
2250            return false;
2251        }
2252        if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
2253            return false;
2254        }
2255
2256        // can't find any difference...
2257
return true;
2258
2259    }
2260
2261    /**
2262     * Returns a clone of the plot.
2263     *
2264     * @return A clone.
2265     *
2266     * @throws CloneNotSupportedException if some component of the plot does
2267     * not support cloning.
2268     */

2269    public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
2270
2271        PiePlot clone = (PiePlot) super.clone();
2272        if (clone.dataset != null) {
2273            clone.dataset.addChangeListener(clone);
2274        }
2275        return clone;
2276
2277    }
2278
2279    /**
2280     * Provides serialization support.
2281     *
2282     * @param stream the output stream.
2283     *
2284     * @throws IOException if there is an I/O error.
2285     */

2286    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
2287        stream.defaultWriteObject();
2288        SerialUtilities.writePaint(this.sectionPaint, stream);
2289        SerialUtilities.writePaint(this.baseSectionPaint, stream);
2290        SerialUtilities.writePaint(this.sectionOutlinePaint, stream);
2291        SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream);
2292        SerialUtilities.writeStroke(this.sectionOutlineStroke, stream);
2293        SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream);
2294        SerialUtilities.writePaint(this.shadowPaint, stream);
2295        SerialUtilities.writePaint(this.labelPaint, stream);
2296        SerialUtilities.writePaint(this.labelBackgroundPaint, stream);
2297        SerialUtilities.writePaint(this.labelOutlinePaint, stream);
2298        SerialUtilities.writeStroke(this.labelOutlineStroke, stream);
2299        SerialUtilities.writePaint(this.labelShadowPaint, stream);
2300        SerialUtilities.writePaint(this.labelLinkPaint, stream);
2301        SerialUtilities.writeStroke(this.labelLinkStroke, stream);
2302        SerialUtilities.writeShape(this.legendItemShape, stream);
2303    }
2304
2305    /**
2306     * Provides serialization support.
2307     *
2308     * @param stream the input stream.
2309     *
2310     * @throws IOException if there is an I/O error.
2311     * @throws ClassNotFoundException if there is a classpath problem.
2312     */

2313    private void readObject(ObjectInputStream JavaDoc stream)
2314        throws IOException JavaDoc, ClassNotFoundException JavaDoc {
2315        stream.defaultReadObject();
2316        this.sectionPaint = SerialUtilities.readPaint(stream);
2317        this.baseSectionPaint = SerialUtilities.readPaint(stream);
2318        this.sectionOutlinePaint = SerialUtilities.readPaint(stream);
2319        this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream);
2320        this.sectionOutlineStroke = SerialUtilities.readStroke(stream);
2321        this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream);
2322        this.shadowPaint = SerialUtilities.readPaint(stream);
2323        this.labelPaint = SerialUtilities.readPaint(stream);
2324        this.labelBackgroundPaint = SerialUtilities.readPaint(stream);
2325        this.labelOutlinePaint = SerialUtilities.readPaint(stream);
2326        this.labelOutlineStroke = SerialUtilities.readStroke(stream);
2327        this.labelShadowPaint = SerialUtilities.readPaint(stream);
2328        this.labelLinkPaint = SerialUtilities.readPaint(stream);
2329        this.labelLinkStroke = SerialUtilities.readStroke(stream);
2330        this.legendItemShape = SerialUtilities.readShape(stream);
2331    }
2332
2333}
2334
Popular Tags