001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it
010     * under the terms of the GNU Lesser General Public License as published by
011     * the Free Software Foundation; either version 2.1 of the License, or
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
022     * USA.
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * ------------
028     * PiePlot.java
029     * ------------
030     * (C) Copyright 2000-2008, by Andrzej Porebski and Contributors.
031     *
032     * Original Author:  Andrzej Porebski;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *                   Martin Cordova (percentages in labels);
035     *                   Richard Atkinson (URL support for image maps);
036     *                   Christian W. Zuckschwerdt;
037     *                   Arnaud Lelievre;
038     *                   Martin Hilpert (patch 1891849);
039     *                   Andreas Schroeder (very minor);
040     *
041     * Changes
042     * -------
043     * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
044     * 18-Sep-2001 : Updated header (DG);
045     * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
046     * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to
047     *               Plot.java (DG);
048     * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
049     * 13-Nov-2001 : Modified plot subclasses so that null axes are possible for
050     *               pie plot (DG);
051     * 17-Nov-2001 : Added PieDataset interface and amended this class accordingly,
052     *               and completed removal of BlankAxis class as it is no longer
053     *               required (DG);
054     * 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG);
055     * 21-Nov-2001 : Added options for exploding pie sections and filled out range
056     *               of properties (DG);
057     *               Added option for percentages in chart labels, based on code
058     *               by Martin Cordova (DG);
059     * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
060     * 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG);
061     * 13-Dec-2001 : Added tooltips (DG);
062     * 16-Jan-2002 : Renamed tooltips class (DG);
063     * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
064     * 05-Feb-2002 : Added alpha-transparency to plot class, and updated
065     *               constructors accordingly (DG);
066     * 06-Feb-2002 : Added optional background image and alpha-transparency to Plot
067     *               and subclasses.  Clipped drawing within plot area (DG);
068     * 26-Mar-2002 : Added an empty zoom method (DG);
069     * 18-Apr-2002 : PieDataset is no longer sorted (oldman);
070     * 23-Apr-2002 : Moved dataset from JFreeChart to Plot.  Added
071     *               getLegendItemLabels() method (DG);
072     * 19-Jun-2002 : Added attributes to control starting angle and direction
073     *               (default is now clockwise) (DG);
074     * 25-Jun-2002 : Removed redundant imports (DG);
075     * 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG);
076     * 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG);
077     * 30-Jul-2002 : Moved summation code to DatasetUtilities (DG);
078     * 05-Aug-2002 : Added URL support for image maps - new member variable for
079     *               urlGenerator, modified constructor and minor change to the
080     *               draw method (RA);
081     * 18-Sep-2002 : Modified the percent label creation and added setters for the
082     *               formatters (AS);
083     * 24-Sep-2002 : Added getLegendItems() method (DG);
084     * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
085     * 09-Oct-2002 : Added check for null entity collection (DG);
086     * 30-Oct-2002 : Changed PieDataset interface (DG);
087     * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
088     * 02-Jan-2003 : Fixed "no data" message (DG);
089     * 23-Jan-2003 : Modified to extract data from rows OR columns in
090     *               CategoryDataset (DG);
091     * 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply
092     *               (bug id 685536) (DG);
093     * 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip
094     *               and URL generators (DG);
095     * 21-Mar-2003 : Added a minimum angle for drawing arcs
096     *               (see bug id 620031) (DG);
097     * 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG);
098     * 02-Jun-2003 : Fixed bug 721733 (DG);
099     * 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     * 08-Jun-2005 : Fixed bug in getLegendItems() method (not respecting flags
129     *               for ignoring null and zero values), and fixed equals() method
130     *               to handle GradientPaint (DG);
131     * 15-Jul-2005 : Added sectionOutlinesVisible attribute (DG);
132     * ------------- JFREECHART 1.0.x ---------------------------------------------
133     * 09-Jan-2006 : Fixed bug 1400442, inconsistent treatment of null and zero
134     *               values in dataset (DG);
135     * 28-Feb-2006 : Fixed bug 1440415, bad distribution of pie section
136     *               labels (DG);
137     * 27-Sep-2006 : Initialised baseSectionPaint correctly, added lookup methods
138     *               for section paint, outline paint and outline stroke (DG);
139     * 27-Sep-2006 : Refactored paint and stroke methods to use keys rather than
140     *               section indices (DG);
141     * 03-Oct-2006 : Replaced call to JRE 1.5 method (DG);
142     * 23-Nov-2006 : Added support for URLs for the legend items (DG);
143     * 24-Nov-2006 : Cloning fixes (DG);
144     * 17-Apr-2007 : Check for null label in legend items (DG);
145     * 19-Apr-2007 : Deprecated override settings (DG);
146     * 18-May-2007 : Set dataset for LegendItem (DG);
147     * 14-Jun-2007 : Added label distributor attribute (DG);
148     * 18-Jul-2007 : Added simple label option (DG);
149     * 21-Nov-2007 : Fixed labelling bugs, added debug code, restored default
150     *               white background (DG);
151     * 19-Mar-2008 : Fixed IllegalArgumentException when drawing with null
152     *               dataset (DG);
153     * 31-Mar-2008 : Adjust the label area for the interiorGap (DG);
154     * 31-Mar-2008 : Added quad and cubic curve label link lines - see patch
155     *               1891849 by Martin Hilpert (DG);
156     * 02-Jul-2008 : Added autoPopulate flags (DG);
157     * 15-Aug-2008 : Added methods to clear section attributes (DG);
158     * 15-Aug-2008 : Fixed bug 2051168 - problem with LegendItemEntity
159     *               generation (DG);
160     *
161     */
162    
163    package org.jfree.chart.plot;
164    
165    import java.awt.AlphaComposite;
166    import java.awt.BasicStroke;
167    import java.awt.Color;
168    import java.awt.Composite;
169    import java.awt.Font;
170    import java.awt.FontMetrics;
171    import java.awt.Graphics2D;
172    import java.awt.Paint;
173    import java.awt.Shape;
174    import java.awt.Stroke;
175    import java.awt.geom.Arc2D;
176    import java.awt.geom.CubicCurve2D;
177    import java.awt.geom.Ellipse2D;
178    import java.awt.geom.Line2D;
179    import java.awt.geom.Point2D;
180    import java.awt.geom.QuadCurve2D;
181    import java.awt.geom.Rectangle2D;
182    import java.io.IOException;
183    import java.io.ObjectInputStream;
184    import java.io.ObjectOutputStream;
185    import java.io.Serializable;
186    import java.util.Iterator;
187    import java.util.List;
188    import java.util.Map;
189    import java.util.ResourceBundle;
190    import java.util.TreeMap;
191    
192    import org.jfree.chart.LegendItem;
193    import org.jfree.chart.LegendItemCollection;
194    import org.jfree.chart.PaintMap;
195    import org.jfree.chart.StrokeMap;
196    import org.jfree.chart.entity.EntityCollection;
197    import org.jfree.chart.entity.PieSectionEntity;
198    import org.jfree.chart.event.PlotChangeEvent;
199    import org.jfree.chart.labels.PieSectionLabelGenerator;
200    import org.jfree.chart.labels.PieToolTipGenerator;
201    import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
202    import org.jfree.chart.urls.PieURLGenerator;
203    import org.jfree.data.DefaultKeyedValues;
204    import org.jfree.data.KeyedValues;
205    import org.jfree.data.general.DatasetChangeEvent;
206    import org.jfree.data.general.DatasetUtilities;
207    import org.jfree.data.general.PieDataset;
208    import org.jfree.io.SerialUtilities;
209    import org.jfree.text.G2TextMeasurer;
210    import org.jfree.text.TextBlock;
211    import org.jfree.text.TextBox;
212    import org.jfree.text.TextUtilities;
213    import org.jfree.ui.RectangleAnchor;
214    import org.jfree.ui.RectangleInsets;
215    import org.jfree.ui.TextAnchor;
216    import org.jfree.util.ObjectUtilities;
217    import org.jfree.util.PaintUtilities;
218    import org.jfree.util.PublicCloneable;
219    import org.jfree.util.Rotation;
220    import org.jfree.util.ShapeUtilities;
221    import org.jfree.util.UnitType;
222    
223    /**
224     * A plot that displays data in the form of a pie chart, using data from any
225     * class that implements the {@link PieDataset} interface.
226     * The example shown here is generated by the <code>PieChartDemo2.java</code>
227     * program included in the JFreeChart Demo Collection:
228     * <br><br>
229     * <img src="../../../../images/PiePlotSample.png"
230     * alt="PiePlotSample.png" />
231     * <P>
232     * Special notes:
233     * <ol>
234     * <li>the default starting point is 12 o'clock and the pie sections proceed
235     * in a clockwise direction, but these settings can be changed;</li>
236     * <li>negative values in the dataset are ignored;</li>
237     * <li>there are utility methods for creating a {@link PieDataset} from a
238     * {@link org.jfree.data.category.CategoryDataset};</li>
239     * </ol>
240     *
241     * @see Plot
242     * @see PieDataset
243     */
244    public class PiePlot extends Plot implements Cloneable, Serializable {
245    
246        /** For serialization. */
247        private static final long serialVersionUID = -795612466005590431L;
248    
249        /** The default interior gap. */
250        public static final double DEFAULT_INTERIOR_GAP = 0.08;
251    
252        /** The maximum interior gap (currently 40%). */
253        public static final double MAX_INTERIOR_GAP = 0.40;
254    
255        /** The default starting angle for the pie chart. */
256        public static final double DEFAULT_START_ANGLE = 90.0;
257    
258        /** The default section label font. */
259        public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
260                Font.PLAIN, 10);
261    
262        /** The default section label paint. */
263        public static final Paint DEFAULT_LABEL_PAINT = Color.black;
264    
265        /** The default section label background paint. */
266        public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255,
267                255, 192);
268    
269        /** The default section label outline paint. */
270        public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
271    
272        /** The default section label outline stroke. */
273        public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(
274                0.5f);
275    
276        /** The default section label shadow paint. */
277        public static final Paint DEFAULT_LABEL_SHADOW_PAINT = new Color(151, 151,
278                151, 128);
279    
280        /** The default minimum arc angle to draw. */
281        public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
282    
283        /** The dataset for the pie chart. */
284        private PieDataset dataset;
285    
286        /** The pie index (used by the {@link MultiplePiePlot} class). */
287        private int pieIndex;
288    
289        /**
290         * The amount of space left around the outside of the pie plot, expressed
291         * as a percentage of the plot area width and height.
292         */
293        private double interiorGap;
294    
295        /** Flag determining whether to draw an ellipse or a perfect circle. */
296        private boolean circular;
297    
298        /** The starting angle. */
299        private double startAngle;
300    
301        /** The direction for the pie segments. */
302        private Rotation direction;
303    
304        /** The section paint map. */
305        private PaintMap sectionPaintMap;
306    
307        /** The base section paint (fallback). */
308        private transient Paint baseSectionPaint;
309    
310        /**
311         * A flag that controls whether or not the section paint is auto-populated
312         * from the drawing supplier.
313         *
314         * @since 1.0.11
315         */
316        private boolean autoPopulateSectionPaint;
317    
318        /**
319         * A flag that controls whether or not an outline is drawn for each
320         * section in the plot.
321         */
322        private boolean sectionOutlinesVisible;
323    
324        /** The section outline paint map. */
325        private PaintMap sectionOutlinePaintMap;
326    
327        /** The base section outline paint (fallback). */
328        private transient Paint baseSectionOutlinePaint;
329    
330        /**
331         * A flag that controls whether or not the section outline paint is
332         * auto-populated from the drawing supplier.
333         *
334         * @since 1.0.11
335         */
336        private boolean autoPopulateSectionOutlinePaint;
337    
338        /** The section outline stroke map. */
339        private StrokeMap sectionOutlineStrokeMap;
340    
341        /** The base section outline stroke (fallback). */
342        private transient Stroke baseSectionOutlineStroke;
343    
344        /**
345         * A flag that controls whether or not the section outline stroke is
346         * auto-populated from the drawing supplier.
347         *
348         * @since 1.0.11
349         */
350        private boolean autoPopulateSectionOutlineStroke;
351    
352        /** The shadow paint. */
353        private transient Paint shadowPaint = Color.gray;
354    
355        /** The x-offset for the shadow effect. */
356        private double shadowXOffset = 4.0f;
357    
358        /** The y-offset for the shadow effect. */
359        private double shadowYOffset = 4.0f;
360    
361        /** The percentage amount to explode each pie section. */
362        private Map explodePercentages;
363    
364        /** The section label generator. */
365        private PieSectionLabelGenerator labelGenerator;
366    
367        /** The font used to display the section labels. */
368        private Font labelFont;
369    
370        /** The color used to draw the section labels. */
371        private transient Paint labelPaint;
372    
373        /**
374         * The color used to draw the background of the section labels.  If this
375         * is <code>null</code>, the background is not filled.
376         */
377        private transient Paint labelBackgroundPaint;
378    
379        /**
380         * The paint used to draw the outline of the section labels
381         * (<code>null</code> permitted).
382         */
383        private transient Paint labelOutlinePaint;
384    
385        /**
386         * The stroke used to draw the outline of the section labels
387         * (<code>null</code> permitted).
388         */
389        private transient Stroke labelOutlineStroke;
390    
391        /**
392         * The paint used to draw the shadow for the section labels
393         * (<code>null</code> permitted).
394         */
395        private transient Paint labelShadowPaint;
396    
397        /**
398         * A flag that controls whether simple or extended labels are used.
399         *
400         * @since 1.0.7
401         */
402        private boolean simpleLabels = true;
403    
404        /**
405         * The padding between the labels and the label outlines.  This is not
406         * allowed to be <code>null</code>.
407         *
408         * @since 1.0.7
409         */
410        private RectangleInsets labelPadding;
411    
412        /**
413         * The simple label offset.
414         *
415         * @since 1.0.7
416         */
417        private RectangleInsets simpleLabelOffset;
418    
419        /** The maximum label width as a percentage of the plot width. */
420        private double maximumLabelWidth = 0.14;
421    
422        /**
423         * The gap between the labels and the link corner, as a percentage of the
424         * plot width.
425         */
426        private double labelGap = 0.025;
427    
428        /** A flag that controls whether or not the label links are drawn. */
429        private boolean labelLinksVisible;
430    
431        /**
432         * The label link style.
433         *
434         * @since 1.0.10
435         */
436        private PieLabelLinkStyle labelLinkStyle = PieLabelLinkStyle.STANDARD;
437    
438        /** The link margin. */
439        private double labelLinkMargin = 0.025;
440    
441        /** The paint used for the label linking lines. */
442        private transient Paint labelLinkPaint = Color.black;
443    
444        /** The stroke used for the label linking lines. */
445        private transient Stroke labelLinkStroke = new BasicStroke(0.5f);
446    
447        /**
448         * The pie section label distributor.
449         *
450         * @since 1.0.6
451         */
452        private AbstractPieLabelDistributor labelDistributor;
453    
454        /** The tooltip generator. */
455        private PieToolTipGenerator toolTipGenerator;
456    
457        /** The URL generator. */
458        private PieURLGenerator urlGenerator;
459    
460        /** The legend label generator. */
461        private PieSectionLabelGenerator legendLabelGenerator;
462    
463        /** A tool tip generator for the legend. */
464        private PieSectionLabelGenerator legendLabelToolTipGenerator;
465    
466        /**
467         * A URL generator for the legend items (optional).
468         *
469         * @since 1.0.4.
470         */
471        private PieURLGenerator legendLabelURLGenerator;
472    
473        /**
474         * A flag that controls whether <code>null</code> values are ignored.
475         */
476        private boolean ignoreNullValues;
477    
478        /**
479         * A flag that controls whether zero values are ignored.
480         */
481        private boolean ignoreZeroValues;
482    
483        /** The legend item shape. */
484        private transient Shape legendItemShape;
485    
486        /**
487         * The smallest arc angle that will get drawn (this is to avoid a bug in
488         * various Java implementations that causes the JVM to crash).  See this
489         * link for details:
490         *
491         * http://www.jfree.org/phpBB2/viewtopic.php?t=2707
492         *
493         * ...and this bug report in the Java Bug Parade:
494         *
495         * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html
496         */
497        private double minimumArcAngleToDraw;
498    
499        /** The resourceBundle for the localization. */
500        protected static ResourceBundle localizationResources =
501                ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
502    
503        /**
504         * This debug flag controls whether or not an outline is drawn showing the
505         * interior of the plot region.  This is drawn as a lightGray rectangle
506         * showing the padding provided by the 'interiorGap' setting.
507         */
508        static final boolean DEBUG_DRAW_INTERIOR = false;
509    
510        /**
511         * This debug flag controls whether or not an outline is drawn showing the
512         * link area (in blue) and link ellipse (in yellow).  This controls where
513         * the label links have 'elbow' points.
514         */
515        static final boolean DEBUG_DRAW_LINK_AREA = false;
516    
517        /**
518         * This debug flag controls whether or not an outline is drawn showing
519         * the pie area (in green).
520         */
521        static final boolean DEBUG_DRAW_PIE_AREA = false;
522    
523        /**
524         * Creates a new plot.  The dataset is initially set to <code>null</code>.
525         */
526        public PiePlot() {
527            this(null);
528        }
529    
530        /**
531         * Creates a plot that will draw a pie chart for the specified dataset.
532         *
533         * @param dataset  the dataset (<code>null</code> permitted).
534         */
535        public PiePlot(PieDataset dataset) {
536            super();
537            this.dataset = dataset;
538            if (dataset != null) {
539                dataset.addChangeListener(this);
540            }
541            this.pieIndex = 0;
542    
543            this.interiorGap = DEFAULT_INTERIOR_GAP;
544            this.circular = true;
545            this.startAngle = DEFAULT_START_ANGLE;
546            this.direction = Rotation.CLOCKWISE;
547            this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
548    
549            this.sectionPaint = null;
550            this.sectionPaintMap = new PaintMap();
551            this.baseSectionPaint = Color.gray;
552            this.autoPopulateSectionPaint = true;
553    
554            this.sectionOutlinesVisible = true;
555            this.sectionOutlinePaint = null;
556            this.sectionOutlinePaintMap = new PaintMap();
557            this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
558            this.autoPopulateSectionOutlinePaint = false;
559    
560            this.sectionOutlineStroke = null;
561            this.sectionOutlineStrokeMap = new StrokeMap();
562            this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
563            this.autoPopulateSectionOutlineStroke = false;
564    
565            this.explodePercentages = new TreeMap();
566    
567            this.labelGenerator = new StandardPieSectionLabelGenerator();
568            this.labelFont = DEFAULT_LABEL_FONT;
569            this.labelPaint = DEFAULT_LABEL_PAINT;
570            this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
571            this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
572            this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
573            this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
574            this.labelLinksVisible = true;
575            this.labelDistributor = new PieLabelDistributor(0);
576    
577            this.simpleLabels = false;
578            this.simpleLabelOffset = new RectangleInsets(UnitType.RELATIVE, 0.18,
579                    0.18, 0.18, 0.18);
580            this.labelPadding = new RectangleInsets(2, 2, 2, 2);
581    
582            this.toolTipGenerator = null;
583            this.urlGenerator = null;
584            this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
585            this.legendLabelToolTipGenerator = null;
586            this.legendLabelURLGenerator = null;
587            this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
588    
589            this.ignoreNullValues = false;
590            this.ignoreZeroValues = false;
591        }
592    
593        /**
594         * Returns the dataset.
595         *
596         * @return The dataset (possibly <code>null</code>).
597         *
598         * @see #setDataset(PieDataset)
599         */
600        public PieDataset getDataset() {
601            return this.dataset;
602        }
603    
604        /**
605         * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'.
606         *
607         * @param dataset  the dataset (<code>null</code> permitted).
608         *
609         * @see #getDataset()
610         */
611        public void setDataset(PieDataset dataset) {
612            // if there is an existing dataset, remove the plot from the list of
613            // change listeners...
614            PieDataset existing = this.dataset;
615            if (existing != null) {
616                existing.removeChangeListener(this);
617            }
618    
619            // set the new dataset, and register the chart as a change listener...
620            this.dataset = dataset;
621            if (dataset != null) {
622                setDatasetGroup(dataset.getGroup());
623                dataset.addChangeListener(this);
624            }
625    
626            // send a dataset change event to self...
627            DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
628            datasetChanged(event);
629        }
630    
631        /**
632         * Returns the pie index (this is used by the {@link MultiplePiePlot} class
633         * to track subplots).
634         *
635         * @return The pie index.
636         *
637         * @see #setPieIndex(int)
638         */
639        public int getPieIndex() {
640            return this.pieIndex;
641        }
642    
643        /**
644         * Sets the pie index (this is used by the {@link MultiplePiePlot} class to
645         * track subplots).
646         *
647         * @param index  the index.
648         *
649         * @see #getPieIndex()
650         */
651        public void setPieIndex(int index) {
652            this.pieIndex = index;
653        }
654    
655        /**
656         * Returns the start angle for the first pie section.  This is measured in
657         * degrees starting from 3 o'clock and measuring anti-clockwise.
658         *
659         * @return The start angle.
660         *
661         * @see #setStartAngle(double)
662         */
663        public double getStartAngle() {
664            return this.startAngle;
665        }
666    
667        /**
668         * Sets the starting angle and sends a {@link PlotChangeEvent} to all
669         * registered listeners.  The initial default value is 90 degrees, which
670         * corresponds to 12 o'clock.  A value of zero corresponds to 3 o'clock...
671         * this is the encoding used by Java's Arc2D class.
672         *
673         * @param angle  the angle (in degrees).
674         *
675         * @see #getStartAngle()
676         */
677        public void setStartAngle(double angle) {
678            this.startAngle = angle;
679            fireChangeEvent();
680        }
681    
682        /**
683         * Returns the direction in which the pie sections are drawn (clockwise or
684         * anti-clockwise).
685         *
686         * @return The direction (never <code>null</code>).
687         *
688         * @see #setDirection(Rotation)
689         */
690        public Rotation getDirection() {
691            return this.direction;
692        }
693    
694        /**
695         * Sets the direction in which the pie sections are drawn and sends a
696         * {@link PlotChangeEvent} to all registered listeners.
697         *
698         * @param direction  the direction (<code>null</code> not permitted).
699         *
700         * @see #getDirection()
701         */
702        public void setDirection(Rotation direction) {
703            if (direction == null) {
704                throw new IllegalArgumentException("Null 'direction' argument.");
705            }
706            this.direction = direction;
707            fireChangeEvent();
708    
709        }
710    
711        /**
712         * Returns the interior gap, measured as a percentage of the available
713         * drawing space.
714         *
715         * @return The gap (as a percentage of the available drawing space).
716         *
717         * @see #setInteriorGap(double)
718         */
719        public double getInteriorGap() {
720            return this.interiorGap;
721        }
722    
723        /**
724         * Sets the interior gap and sends a {@link PlotChangeEvent} to all
725         * registered listeners.  This controls the space between the edges of the
726         * pie plot and the plot area itself (the region where the section labels
727         * appear).
728         *
729         * @param percent  the gap (as a percentage of the available drawing space).
730         *
731         * @see #getInteriorGap()
732         */
733        public void setInteriorGap(double percent) {
734    
735            if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
736                throw new IllegalArgumentException(
737                    "Invalid 'percent' (" + percent + ") argument.");
738            }
739    
740            if (this.interiorGap != percent) {
741                this.interiorGap = percent;
742                fireChangeEvent();
743            }
744    
745        }
746    
747        /**
748         * Returns a flag indicating whether the pie chart is circular, or
749         * stretched into an elliptical shape.
750         *
751         * @return A flag indicating whether the pie chart is circular.
752         *
753         * @see #setCircular(boolean)
754         */
755        public boolean isCircular() {
756            return this.circular;
757        }
758    
759        /**
760         * A flag indicating whether the pie chart is circular, or stretched into
761         * an elliptical shape.
762         *
763         * @param flag  the new value.
764         *
765         * @see #isCircular()
766         */
767        public void setCircular(boolean flag) {
768            setCircular(flag, true);
769        }
770    
771        /**
772         * Sets the circular attribute and, if requested, sends a
773         * {@link PlotChangeEvent} to all registered listeners.
774         *
775         * @param circular  the new value of the flag.
776         * @param notify  notify listeners?
777         *
778         * @see #isCircular()
779         */
780        public void setCircular(boolean circular, boolean notify) {
781            this.circular = circular;
782            if (notify) {
783                fireChangeEvent();
784            }
785        }
786    
787        /**
788         * Returns the flag that controls whether <code>null</code> values in the
789         * dataset are ignored.
790         *
791         * @return A boolean.
792         *
793         * @see #setIgnoreNullValues(boolean)
794         */
795        public boolean getIgnoreNullValues() {
796            return this.ignoreNullValues;
797        }
798    
799        /**
800         * Sets a flag that controls whether <code>null</code> values are ignored,
801         * and sends a {@link PlotChangeEvent} to all registered listeners.  At
802         * present, this only affects whether or not the key is presented in the
803         * legend.
804         *
805         * @param flag  the flag.
806         *
807         * @see #getIgnoreNullValues()
808         * @see #setIgnoreZeroValues(boolean)
809         */
810        public void setIgnoreNullValues(boolean flag) {
811            this.ignoreNullValues = flag;
812            fireChangeEvent();
813        }
814    
815        /**
816         * Returns the flag that controls whether zero values in the
817         * dataset are ignored.
818         *
819         * @return A boolean.
820         *
821         * @see #setIgnoreZeroValues(boolean)
822         */
823        public boolean getIgnoreZeroValues() {
824            return this.ignoreZeroValues;
825        }
826    
827        /**
828         * Sets a flag that controls whether zero values are ignored,
829         * and sends a {@link PlotChangeEvent} to all registered listeners.  This
830         * only affects whether or not a label appears for the non-visible
831         * pie section.
832         *
833         * @param flag  the flag.
834         *
835         * @see #getIgnoreZeroValues()
836         * @see #setIgnoreNullValues(boolean)
837         */
838        public void setIgnoreZeroValues(boolean flag) {
839            this.ignoreZeroValues = flag;
840            fireChangeEvent();
841        }
842    
843        //// SECTION PAINT ////////////////////////////////////////////////////////
844    
845        /**
846         * Returns the paint for the specified section.  This is equivalent to
847         * <code>lookupSectionPaint(section, getAutoPopulateSectionPaint())</code>.
848         *
849         * @param key  the section key.
850         *
851         * @return The paint for the specified section.
852         *
853         * @since 1.0.3
854         *
855         * @see #lookupSectionPaint(Comparable, boolean)
856         */
857        protected Paint lookupSectionPaint(Comparable key) {
858            return lookupSectionPaint(key, getAutoPopulateSectionPaint());
859        }
860    
861        /**
862         * Returns the paint for the specified section.  The lookup involves these
863         * steps:
864         * <ul>
865         * <li>if {@link #getSectionPaint()} is non-<code>null</code>, return
866         *         it;</li>
867         * <li>if {@link #getSectionPaint(int)} is non-<code>null</code> return
868         *         it;</li>
869         * <li>if {@link #getSectionPaint(int)} is <code>null</code> but
870         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
871         *         a new paint from the drawing supplier
872         *         ({@link #getDrawingSupplier()});
873         * <li>if all else fails, return {@link #getBaseSectionPaint()}.
874         * </ul>
875         *
876         * @param key  the section key.
877         * @param autoPopulate  a flag that controls whether the drawing supplier
878         *     is used to auto-populate the section paint settings.
879         *
880         * @return The paint.
881         *
882         * @since 1.0.3
883         */
884        protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) {
885    
886            // is there an override?
887            Paint result = getSectionPaint();
888            if (result != null) {
889                return result;
890            }
891    
892            // if not, check if there is a paint defined for the specified key
893            result = this.sectionPaintMap.getPaint(key);
894            if (result != null) {
895                return result;
896            }
897    
898            // nothing defined - do we autoPopulate?
899            if (autoPopulate) {
900                DrawingSupplier ds = getDrawingSupplier();
901                if (ds != null) {
902                    result = ds.getNextPaint();
903                    this.sectionPaintMap.put(key, result);
904                }
905                else {
906                    result = this.baseSectionPaint;
907                }
908            }
909            else {
910                result = this.baseSectionPaint;
911            }
912            return result;
913        }
914    
915        /**
916         * Returns the paint for ALL sections in the plot.
917         *
918         * @return The paint (possibly <code>null</code>).
919         *
920         * @see #setSectionPaint(Paint)
921         *
922         * @deprecated Use {@link #getSectionPaint(Comparable)} and
923         *     {@link #getBaseSectionPaint()}.  Deprecated as of version 1.0.6.
924         */
925        public Paint getSectionPaint() {
926            return this.sectionPaint;
927        }
928    
929        /**
930         * Sets the paint for ALL sections in the plot.  If this is set to
931         * </code>null</code>, then a list of paints is used instead (to allow
932         * different colors to be used for each section).
933         *
934         * @param paint  the paint (<code>null</code> permitted).
935         *
936         * @see #getSectionPaint()
937         *
938         * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} and
939         *     {@link #setBaseSectionPaint(Paint)}.  Deprecated as of version 1.0.6.
940         */
941        public void setSectionPaint(Paint paint) {
942            this.sectionPaint = paint;
943            fireChangeEvent();
944        }
945    
946        /**
947         * Returns a key for the specified section.  If there is no such section
948         * in the dataset, we generate a key.  This is to provide some backward
949         * compatibility for the (now deprecated) methods that get/set attributes
950         * based on section indices.  The preferred way of doing this now is to
951         * link the attributes directly to the section key (there are new methods
952         * for this, starting from version 1.0.3).
953         *
954         * @param section  the section index.
955         *
956         * @return The key.
957         *
958         * @since 1.0.3
959         */
960        protected Comparable getSectionKey(int section) {
961            Comparable key = null;
962            if (this.dataset != null) {
963                if (section >= 0 && section < this.dataset.getItemCount()) {
964                    key = this.dataset.getKey(section);
965                }
966            }
967            if (key == null) {
968                key = new Integer(section);
969            }
970            return key;
971        }
972    
973        /**
974         * Returns the paint associated with the specified key, or
975         * <code>null</code> if there is no paint associated with the key.
976         *
977         * @param key  the key (<code>null</code> not permitted).
978         *
979         * @return The paint associated with the specified key, or
980         *     <code>null</code>.
981         *
982         * @throws IllegalArgumentException if <code>key</code> is
983         *     <code>null</code>.
984         *
985         * @see #setSectionPaint(Comparable, Paint)
986         *
987         * @since 1.0.3
988         */
989        public Paint getSectionPaint(Comparable key) {
990            // null argument check delegated...
991            return this.sectionPaintMap.getPaint(key);
992        }
993    
994        /**
995         * Sets the paint associated with the specified key, and sends a
996         * {@link PlotChangeEvent} to all registered listeners.
997         *
998         * @param key  the key (<code>null</code> not permitted).
999         * @param paint  the paint.
1000         *
1001         * @throws IllegalArgumentException if <code>key</code> is
1002         *     <code>null</code>.
1003         *
1004         * @see #getSectionPaint(Comparable)
1005         *
1006         * @since 1.0.3
1007         */
1008        public void setSectionPaint(Comparable key, Paint paint) {
1009            // null argument check delegated...
1010            this.sectionPaintMap.put(key, paint);
1011            fireChangeEvent();
1012        }
1013    
1014        /**
1015         * Clears the section paint settings for this plot and, if requested, sends
1016         * a {@link PlotChangeEvent} to all registered listeners.  Be aware that
1017         * if the <code>autoPopulateSectionPaint</code> flag is set, the section
1018         * paints may be repopulated using the same colours as before.
1019         *
1020         * @param notify  notify listeners?
1021         *
1022         * @since 1.0.11
1023         *
1024         * @see #autoPopulateSectionPaint
1025         */
1026        public void clearSectionPaints(boolean notify) {
1027            this.sectionPaintMap.clear();
1028            if (notify) {
1029                fireChangeEvent();
1030            }
1031        }
1032    
1033        /**
1034         * Returns the base section paint.  This is used when no other paint is
1035         * defined, which is rare.  The default value is <code>Color.gray</code>.
1036         *
1037         * @return The paint (never <code>null</code>).
1038         *
1039         * @see #setBaseSectionPaint(Paint)
1040         */
1041        public Paint getBaseSectionPaint() {
1042            return this.baseSectionPaint;
1043        }
1044    
1045        /**
1046         * Sets the base section paint and sends a {@link PlotChangeEvent} to all
1047         * registered listeners.
1048         *
1049         * @param paint  the paint (<code>null</code> not permitted).
1050         *
1051         * @see #getBaseSectionPaint()
1052         */
1053        public void setBaseSectionPaint(Paint paint) {
1054            if (paint == null) {
1055                throw new IllegalArgumentException("Null 'paint' argument.");
1056            }
1057            this.baseSectionPaint = paint;
1058            fireChangeEvent();
1059        }
1060    
1061        /**
1062         * Returns the flag that controls whether or not the section paint is
1063         * auto-populated by the {@link #lookupSectionPaint(Comparable)} method.
1064         *
1065         * @return A boolean.
1066         *
1067         * @since 1.0.11
1068         */
1069        public boolean getAutoPopulateSectionPaint() {
1070            return this.autoPopulateSectionPaint;
1071        }
1072    
1073        /**
1074         * Sets the flag that controls whether or not the section paint is
1075         * auto-populated by the {@link #lookupSectionPaint(Comparable)} method,
1076         * and sends a {@link PlotChangeEvent} to all registered listeners.
1077         *
1078         * @param auto  auto-populate?
1079         *
1080         * @since 1.0.11
1081         */
1082        public void setAutoPopulateSectionPaint(boolean auto) {
1083            this.autoPopulateSectionPaint = auto;
1084            fireChangeEvent();
1085        }
1086    
1087        //// SECTION OUTLINE PAINT ////////////////////////////////////////////////
1088    
1089        /**
1090         * Returns the flag that controls whether or not the outline is drawn for
1091         * each pie section.
1092         *
1093         * @return The flag that controls whether or not the outline is drawn for
1094         *         each pie section.
1095         *
1096         * @see #setSectionOutlinesVisible(boolean)
1097         */
1098        public boolean getSectionOutlinesVisible() {
1099            return this.sectionOutlinesVisible;
1100        }
1101    
1102        /**
1103         * Sets the flag that controls whether or not the outline is drawn for
1104         * each pie section, and sends a {@link PlotChangeEvent} to all registered
1105         * listeners.
1106         *
1107         * @param visible  the flag.
1108         *
1109         * @see #getSectionOutlinesVisible()
1110         */
1111        public void setSectionOutlinesVisible(boolean visible) {
1112            this.sectionOutlinesVisible = visible;
1113            fireChangeEvent();
1114        }
1115    
1116        /**
1117         * Returns the outline paint for the specified section.  This is equivalent
1118         * to <code>lookupSectionPaint(section,
1119         * getAutoPopulateSectionOutlinePaint())</code>.
1120         *
1121         * @param key  the section key.
1122         *
1123         * @return The paint for the specified section.
1124         *
1125         * @since 1.0.3
1126         *
1127         * @see #lookupSectionOutlinePaint(Comparable, boolean)
1128         */
1129        protected Paint lookupSectionOutlinePaint(Comparable key) {
1130            return lookupSectionOutlinePaint(key,
1131                    getAutoPopulateSectionOutlinePaint());
1132        }
1133    
1134        /**
1135         * Returns the outline paint for the specified section.  The lookup
1136         * involves these steps:
1137         * <ul>
1138         * <li>if {@link #getSectionOutlinePaint()} is non-<code>null</code>,
1139         *         return it;</li>
1140         * <li>otherwise, if {@link #getSectionOutlinePaint(int)} is
1141         *         non-<code>null</code> return it;</li>
1142         * <li>if {@link #getSectionOutlinePaint(int)} is <code>null</code> but
1143         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1144         *         a new outline paint from the drawing supplier
1145         *         ({@link #getDrawingSupplier()});
1146         * <li>if all else fails, return {@link #getBaseSectionOutlinePaint()}.
1147         * </ul>
1148         *
1149         * @param key  the section key.
1150         * @param autoPopulate  a flag that controls whether the drawing supplier
1151         *     is used to auto-populate the section outline paint settings.
1152         *
1153         * @return The paint.
1154         *
1155         * @since 1.0.3
1156         */
1157        protected Paint lookupSectionOutlinePaint(Comparable key,
1158                boolean autoPopulate) {
1159    
1160            // is there an override?
1161            Paint result = getSectionOutlinePaint();
1162            if (result != null) {
1163                return result;
1164            }
1165    
1166            // if not, check if there is a paint defined for the specified key
1167            result = this.sectionOutlinePaintMap.getPaint(key);
1168            if (result != null) {
1169                return result;
1170            }
1171    
1172            // nothing defined - do we autoPopulate?
1173            if (autoPopulate) {
1174                DrawingSupplier ds = getDrawingSupplier();
1175                if (ds != null) {
1176                    result = ds.getNextOutlinePaint();
1177                    this.sectionOutlinePaintMap.put(key, result);
1178                }
1179                else {
1180                    result = this.baseSectionOutlinePaint;
1181                }
1182            }
1183            else {
1184                result = this.baseSectionOutlinePaint;
1185            }
1186            return result;
1187        }
1188    
1189        /**
1190         * Returns the outline paint associated with the specified key, or
1191         * <code>null</code> if there is no paint associated with the key.
1192         *
1193         * @param key  the key (<code>null</code> not permitted).
1194         *
1195         * @return The paint associated with the specified key, or
1196         *     <code>null</code>.
1197         *
1198         * @throws IllegalArgumentException if <code>key</code> is
1199         *     <code>null</code>.
1200         *
1201         * @see #setSectionOutlinePaint(Comparable, Paint)
1202         *
1203         * @since 1.0.3
1204         */
1205        public Paint getSectionOutlinePaint(Comparable key) {
1206            // null argument check delegated...
1207            return this.sectionOutlinePaintMap.getPaint(key);
1208        }
1209    
1210        /**
1211         * Sets the outline paint associated with the specified key, and sends a
1212         * {@link PlotChangeEvent} to all registered listeners.
1213         *
1214         * @param key  the key (<code>null</code> not permitted).
1215         * @param paint  the paint.
1216         *
1217         * @throws IllegalArgumentException if <code>key</code> is
1218         *     <code>null</code>.
1219         *
1220         * @see #getSectionOutlinePaint(Comparable)
1221         *
1222         * @since 1.0.3
1223         */
1224        public void setSectionOutlinePaint(Comparable key, Paint paint) {
1225            // null argument check delegated...
1226            this.sectionOutlinePaintMap.put(key, paint);
1227            fireChangeEvent();
1228        }
1229    
1230        /**
1231         * Clears the section outline paint settings for this plot and, if
1232         * requested, sends a {@link PlotChangeEvent} to all registered listeners.
1233         * Be aware that if the <code>autoPopulateSectionPaint</code> flag is set,
1234         * the section paints may be repopulated using the same colours as before.
1235         *
1236         * @param notify  notify listeners?
1237         *
1238         * @since 1.0.11
1239         *
1240         * @see #autoPopulateSectionOutlinePaint
1241         */
1242        public void clearSectionOutlinePaints(boolean notify) {
1243            this.sectionOutlinePaintMap.clear();
1244            if (notify) {
1245                fireChangeEvent();
1246            }
1247        }
1248    
1249        /**
1250         * Returns the base section paint.  This is used when no other paint is
1251         * available.
1252         *
1253         * @return The paint (never <code>null</code>).
1254         *
1255         * @see #setBaseSectionOutlinePaint(Paint)
1256         */
1257        public Paint getBaseSectionOutlinePaint() {
1258            return this.baseSectionOutlinePaint;
1259        }
1260    
1261        /**
1262         * Sets the base section paint.
1263         *
1264         * @param paint  the paint (<code>null</code> not permitted).
1265         *
1266         * @see #getBaseSectionOutlinePaint()
1267         */
1268        public void setBaseSectionOutlinePaint(Paint paint) {
1269            if (paint == null) {
1270                throw new IllegalArgumentException("Null 'paint' argument.");
1271            }
1272            this.baseSectionOutlinePaint = paint;
1273            fireChangeEvent();
1274        }
1275    
1276        /**
1277         * Returns the flag that controls whether or not the section outline paint
1278         * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)}
1279         * method.
1280         *
1281         * @return A boolean.
1282         *
1283         * @since 1.0.11
1284         */
1285        public boolean getAutoPopulateSectionOutlinePaint() {
1286            return this.autoPopulateSectionOutlinePaint;
1287        }
1288    
1289        /**
1290         * Sets the flag that controls whether or not the section outline paint is
1291         * auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)}
1292         * method, and sends a {@link PlotChangeEvent} to all registered listeners.
1293         *
1294         * @param auto  auto-populate?
1295         *
1296         * @since 1.0.11
1297         */
1298        public void setAutoPopulateSectionOutlinePaint(boolean auto) {
1299            this.autoPopulateSectionOutlinePaint = auto;
1300            fireChangeEvent();
1301        }
1302    
1303        //// SECTION OUTLINE STROKE ///////////////////////////////////////////////
1304    
1305        /**
1306         * Returns the outline stroke for the specified section.  This is
1307         * equivalent to <code>lookupSectionOutlineStroke(section,
1308         * getAutoPopulateSectionOutlineStroke())</code>.
1309         *
1310         * @param key  the section key.
1311         *
1312         * @return The stroke for the specified section.
1313         *
1314         * @since 1.0.3
1315         *
1316         * @see #lookupSectionOutlineStroke(Comparable, boolean)
1317         */
1318        protected Stroke lookupSectionOutlineStroke(Comparable key) {
1319            return lookupSectionOutlineStroke(key,
1320                    getAutoPopulateSectionOutlineStroke());
1321        }
1322    
1323        /**
1324         * Returns the outline stroke for the specified section.  The lookup
1325         * involves these steps:
1326         * <ul>
1327         * <li>if {@link #getSectionOutlineStroke()} is non-<code>null</code>,
1328         *         return it;</li>
1329         * <li>otherwise, if {@link #getSectionOutlineStroke(int)} is
1330         *         non-<code>null</code> return it;</li>
1331         * <li>if {@link #getSectionOutlineStroke(int)} is <code>null</code> but
1332         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1333         *         a new outline stroke from the drawing supplier
1334         *         ({@link #getDrawingSupplier()});
1335         * <li>if all else fails, return {@link #getBaseSectionOutlineStroke()}.
1336         * </ul>
1337         *
1338         * @param key  the section key.
1339         * @param autoPopulate  a flag that controls whether the drawing supplier
1340         *     is used to auto-populate the section outline stroke settings.
1341         *
1342         * @return The stroke.
1343         *
1344         * @since 1.0.3
1345         */
1346        protected Stroke lookupSectionOutlineStroke(Comparable key,
1347                boolean autoPopulate) {
1348    
1349            // is there an override?
1350            Stroke result = getSectionOutlineStroke();
1351            if (result != null) {
1352                return result;
1353            }
1354    
1355            // if not, check if there is a stroke defined for the specified key
1356            result = this.sectionOutlineStrokeMap.getStroke(key);
1357            if (result != null) {
1358                return result;
1359            }
1360    
1361            // nothing defined - do we autoPopulate?
1362            if (autoPopulate) {
1363                DrawingSupplier ds = getDrawingSupplier();
1364                if (ds != null) {
1365                    result = ds.getNextOutlineStroke();
1366                    this.sectionOutlineStrokeMap.put(key, result);
1367                }
1368                else {
1369                    result = this.baseSectionOutlineStroke;
1370                }
1371            }
1372            else {
1373                result = this.baseSectionOutlineStroke;
1374            }
1375            return result;
1376        }
1377    
1378        /**
1379         * Returns the outline stroke associated with the specified key, or
1380         * <code>null</code> if there is no stroke associated with the key.
1381         *
1382         * @param key  the key (<code>null</code> not permitted).
1383         *
1384         * @return The stroke associated with the specified key, or
1385         *     <code>null</code>.
1386         *
1387         * @throws IllegalArgumentException if <code>key</code> is
1388         *     <code>null</code>.
1389         *
1390         * @see #setSectionOutlineStroke(Comparable, Stroke)
1391         *
1392         * @since 1.0.3
1393         */
1394        public Stroke getSectionOutlineStroke(Comparable key) {
1395            // null argument check delegated...
1396            return this.sectionOutlineStrokeMap.getStroke(key);
1397        }
1398    
1399        /**
1400         * Sets the outline stroke associated with the specified key, and sends a
1401         * {@link PlotChangeEvent} to all registered listeners.
1402         *
1403         * @param key  the key (<code>null</code> not permitted).
1404         * @param stroke  the stroke.
1405         *
1406         * @throws IllegalArgumentException if <code>key</code> is
1407         *     <code>null</code>.
1408         *
1409         * @see #getSectionOutlineStroke(Comparable)
1410         *
1411         * @since 1.0.3
1412         */
1413        public void setSectionOutlineStroke(Comparable key, Stroke stroke) {
1414            // null argument check delegated...
1415            this.sectionOutlineStrokeMap.put(key, stroke);
1416            fireChangeEvent();
1417        }
1418    
1419        /**
1420         * Clears the section outline stroke settings for this plot and, if
1421         * requested, sends a {@link PlotChangeEvent} to all registered listeners.
1422         * Be aware that if the <code>autoPopulateSectionPaint</code> flag is set,
1423         * the section paints may be repopulated using the same colours as before.
1424         *
1425         * @param notify  notify listeners?
1426         *
1427         * @since 1.0.11
1428         *
1429         * @see #autoPopulateSectionOutlineStroke
1430         */
1431        public void clearSectionOutlineStrokes(boolean notify) {
1432            this.sectionOutlineStrokeMap.clear();
1433            if (notify) {
1434                fireChangeEvent();
1435            }
1436        }
1437    
1438        /**
1439         * Returns the base section stroke.  This is used when no other stroke is
1440         * available.
1441         *
1442         * @return The stroke (never <code>null</code>).
1443         *
1444         * @see #setBaseSectionOutlineStroke(Stroke)
1445         */
1446        public Stroke getBaseSectionOutlineStroke() {
1447            return this.baseSectionOutlineStroke;
1448        }
1449    
1450        /**
1451         * Sets the base section stroke.
1452         *
1453         * @param stroke  the stroke (<code>null</code> not permitted).
1454         *
1455         * @see #getBaseSectionOutlineStroke()
1456         */
1457        public void setBaseSectionOutlineStroke(Stroke stroke) {
1458            if (stroke == null) {
1459                throw new IllegalArgumentException("Null 'stroke' argument.");
1460            }
1461            this.baseSectionOutlineStroke = stroke;
1462            fireChangeEvent();
1463        }
1464    
1465        /**
1466         * Returns the flag that controls whether or not the section outline stroke
1467         * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)}
1468         * method.
1469         *
1470         * @return A boolean.
1471         *
1472         * @since 1.0.11
1473         */
1474        public boolean getAutoPopulateSectionOutlineStroke() {
1475            return this.autoPopulateSectionOutlineStroke;
1476        }
1477    
1478        /**
1479         * Sets the flag that controls whether or not the section outline stroke is
1480         * auto-populated by the {@link #lookupSectionOutlineStroke(Comparable)}
1481         * method, and sends a {@link PlotChangeEvent} to all registered listeners.
1482         *
1483         * @param auto  auto-populate?
1484         *
1485         * @since 1.0.11
1486         */
1487        public void setAutoPopulateSectionOutlineStroke(boolean auto) {
1488            this.autoPopulateSectionOutlineStroke = auto;
1489            fireChangeEvent();
1490        }
1491    
1492        /**
1493         * Returns the shadow paint.
1494         *
1495         * @return The paint (possibly <code>null</code>).
1496         *
1497         * @see #setShadowPaint(Paint)
1498         */
1499        public Paint getShadowPaint() {
1500            return this.shadowPaint;
1501        }
1502    
1503        /**
1504         * Sets the shadow paint and sends a {@link PlotChangeEvent} to all
1505         * registered listeners.
1506         *
1507         * @param paint  the paint (<code>null</code> permitted).
1508         *
1509         * @see #getShadowPaint()
1510         */
1511        public void setShadowPaint(Paint paint) {
1512            this.shadowPaint = paint;
1513            fireChangeEvent();
1514        }
1515    
1516        /**
1517         * Returns the x-offset for the shadow effect.
1518         *
1519         * @return The offset (in Java2D units).
1520         *
1521         * @see #setShadowXOffset(double)
1522         */
1523        public double getShadowXOffset() {
1524            return this.shadowXOffset;
1525        }
1526    
1527        /**
1528         * Sets the x-offset for the shadow effect and sends a
1529         * {@link PlotChangeEvent} to all registered listeners.
1530         *
1531         * @param offset  the offset (in Java2D units).
1532         *
1533         * @see #getShadowXOffset()
1534         */
1535        public void setShadowXOffset(double offset) {
1536            this.shadowXOffset = offset;
1537            fireChangeEvent();
1538        }
1539    
1540        /**
1541         * Returns the y-offset for the shadow effect.
1542         *
1543         * @return The offset (in Java2D units).
1544         *
1545         * @see #setShadowYOffset(double)
1546         */
1547        public double getShadowYOffset() {
1548            return this.shadowYOffset;
1549        }
1550    
1551        /**
1552         * Sets the y-offset for the shadow effect and sends a
1553         * {@link PlotChangeEvent} to all registered listeners.
1554         *
1555         * @param offset  the offset (in Java2D units).
1556         *
1557         * @see #getShadowYOffset()
1558         */
1559        public void setShadowYOffset(double offset) {
1560            this.shadowYOffset = offset;
1561            fireChangeEvent();
1562        }
1563    
1564        /**
1565         * Returns the amount that the section with the specified key should be
1566         * exploded.
1567         *
1568         * @param key  the key (<code>null</code> not permitted).
1569         *
1570         * @return The amount that the section with the specified key should be
1571         *     exploded.
1572         *
1573         * @throws IllegalArgumentException if <code>key</code> is
1574         *     <code>null</code>.
1575         *
1576         * @since 1.0.3
1577         *
1578         * @see #setExplodePercent(Comparable, double)
1579         */
1580        public double getExplodePercent(Comparable key) {
1581            double result = 0.0;
1582            if (this.explodePercentages != null) {
1583                Number percent = (Number) this.explodePercentages.get(key);
1584                if (percent != null) {
1585                    result = percent.doubleValue();
1586                }
1587            }
1588            return result;
1589        }
1590    
1591        /**
1592         * Sets the amount that a pie section should be exploded and sends a
1593         * {@link PlotChangeEvent} to all registered listeners.
1594         *
1595         * @param key  the section key (<code>null</code> not permitted).
1596         * @param percent  the explode percentage (0.30 = 30 percent).
1597         *
1598         * @since 1.0.3
1599         *
1600         * @see #getExplodePercent(Comparable)
1601         */
1602        public void setExplodePercent(Comparable key, double percent) {
1603            if (key == null) {
1604                throw new IllegalArgumentException("Null 'key' argument.");
1605            }
1606            if (this.explodePercentages == null) {
1607                this.explodePercentages = new TreeMap();
1608            }
1609            this.explodePercentages.put(key, new Double(percent));
1610            fireChangeEvent();
1611        }
1612    
1613        /**
1614         * Returns the maximum explode percent.
1615         *
1616         * @return The percent.
1617         */
1618        public double getMaximumExplodePercent() {
1619            if (this.dataset == null) {
1620                return 0.0;
1621            }
1622            double result = 0.0;
1623            Iterator iterator = this.dataset.getKeys().iterator();
1624            while (iterator.hasNext()) {
1625                Comparable key = (Comparable) iterator.next();
1626                Number explode = (Number) this.explodePercentages.get(key);
1627                if (explode != null) {
1628                    result = Math.max(result, explode.doubleValue());
1629                }
1630            }
1631            return result;
1632        }
1633    
1634        /**
1635         * Returns the section label generator.
1636         *
1637         * @return The generator (possibly <code>null</code>).
1638         *
1639         * @see #setLabelGenerator(PieSectionLabelGenerator)
1640         */
1641        public PieSectionLabelGenerator getLabelGenerator() {
1642            return this.labelGenerator;
1643        }
1644    
1645        /**
1646         * Sets the section label generator and sends a {@link PlotChangeEvent} to
1647         * all registered listeners.
1648         *
1649         * @param generator  the generator (<code>null</code> permitted).
1650         *
1651         * @see #getLabelGenerator()
1652         */
1653        public void setLabelGenerator(PieSectionLabelGenerator generator) {
1654            this.labelGenerator = generator;
1655            fireChangeEvent();
1656        }
1657    
1658        /**
1659         * Returns the gap between the edge of the pie and the labels, expressed as
1660         * a percentage of the plot width.
1661         *
1662         * @return The gap (a percentage, where 0.05 = five percent).
1663         *
1664         * @see #setLabelGap(double)
1665         */
1666        public double getLabelGap() {
1667            return this.labelGap;
1668        }
1669    
1670        /**
1671         * Sets the gap between the edge of the pie and the labels (expressed as a
1672         * percentage of the plot width) and sends a {@link PlotChangeEvent} to all
1673         * registered listeners.
1674         *
1675         * @param gap  the gap (a percentage, where 0.05 = five percent).
1676         *
1677         * @see #getLabelGap()
1678         */
1679        public void setLabelGap(double gap) {
1680            this.labelGap = gap;
1681            fireChangeEvent();
1682        }
1683    
1684        /**
1685         * Returns the maximum label width as a percentage of the plot width.
1686         *
1687         * @return The width (a percentage, where 0.20 = 20 percent).
1688         *
1689         * @see #setMaximumLabelWidth(double)
1690         */
1691        public double getMaximumLabelWidth() {
1692            return this.maximumLabelWidth;
1693        }
1694    
1695        /**
1696         * Sets the maximum label width as a percentage of the plot width and sends
1697         * a {@link PlotChangeEvent} to all registered listeners.
1698         *
1699         * @param width  the width (a percentage, where 0.20 = 20 percent).
1700         *
1701         * @see #getMaximumLabelWidth()
1702         */
1703        public void setMaximumLabelWidth(double width) {
1704            this.maximumLabelWidth = width;
1705            fireChangeEvent();
1706        }
1707    
1708        /**
1709         * Returns the flag that controls whether or not label linking lines are
1710         * visible.
1711         *
1712         * @return A boolean.
1713         *
1714         * @see #setLabelLinksVisible(boolean)
1715         */
1716        public boolean getLabelLinksVisible() {
1717            return this.labelLinksVisible;
1718        }
1719    
1720        /**
1721         * Sets the flag that controls whether or not label linking lines are
1722         * visible and sends a {@link PlotChangeEvent} to all registered listeners.
1723         * Please take care when hiding the linking lines - depending on the data
1724         * values, the labels can be displayed some distance away from the
1725         * corresponding pie section.
1726         *
1727         * @param visible  the flag.
1728         *
1729         * @see #getLabelLinksVisible()
1730         */
1731        public void setLabelLinksVisible(boolean visible) {
1732            this.labelLinksVisible = visible;
1733            fireChangeEvent();
1734        }
1735    
1736        /**
1737         * Returns the label link style.
1738         *
1739         * @return The label link style (never <code>null</code>).
1740         *
1741         * @see #setLabelLinkStyle(PieLabelLinkStyle)
1742         *
1743         * @since 1.0.10
1744         */
1745        public PieLabelLinkStyle getLabelLinkStyle() {
1746            return this.labelLinkStyle;
1747        }
1748    
1749        /**
1750         * Sets the label link style and sends a {@link PlotChangeEvent} to all
1751         * registered listeners.
1752         *
1753         * @param style  the new style (<code>null</code> not permitted).
1754         *
1755         * @see #getLabelLinkStyle()
1756         *
1757         * @since 1.0.10
1758         */
1759        public void setLabelLinkStyle(PieLabelLinkStyle style) {
1760            if (style == null) {
1761                throw new IllegalArgumentException("Null 'style' argument.");
1762            }
1763            this.labelLinkStyle = style;
1764            fireChangeEvent();
1765        }
1766    
1767        /**
1768         * Returns the margin (expressed as a percentage of the width or height)
1769         * between the edge of the pie and the link point.
1770         *
1771         * @return The link margin (as a percentage, where 0.05 is five percent).
1772         *
1773         * @see #setLabelLinkMargin(double)
1774         */
1775        public double getLabelLinkMargin() {
1776            return this.labelLinkMargin;
1777        }
1778    
1779        /**
1780         * Sets the link margin and sends a {@link PlotChangeEvent} to all
1781         * registered listeners.
1782         *
1783         * @param margin  the margin.
1784         *
1785         * @see #getLabelLinkMargin()
1786         */
1787        public void setLabelLinkMargin(double margin) {
1788            this.labelLinkMargin = margin;
1789            fireChangeEvent();
1790        }
1791    
1792        /**
1793         * Returns the paint used for the lines that connect pie sections to their
1794         * corresponding labels.
1795         *
1796         * @return The paint (never <code>null</code>).
1797         *
1798         * @see #setLabelLinkPaint(Paint)
1799         */
1800        public Paint getLabelLinkPaint() {
1801            return this.labelLinkPaint;
1802        }
1803    
1804        /**
1805         * Sets the paint used for the lines that connect pie sections to their
1806         * corresponding labels, and sends a {@link PlotChangeEvent} to all
1807         * registered listeners.
1808         *
1809         * @param paint  the paint (<code>null</code> not permitted).
1810         *
1811         * @see #getLabelLinkPaint()
1812         */
1813        public void setLabelLinkPaint(Paint paint) {
1814            if (paint == null) {
1815                throw new IllegalArgumentException("Null 'paint' argument.");
1816            }
1817            this.labelLinkPaint = paint;
1818            fireChangeEvent();
1819        }
1820    
1821        /**
1822         * Returns the stroke used for the label linking lines.
1823         *
1824         * @return The stroke.
1825         *
1826         * @see #setLabelLinkStroke(Stroke)
1827         */
1828        public Stroke getLabelLinkStroke() {
1829            return this.labelLinkStroke;
1830        }
1831    
1832        /**
1833         * Sets the link stroke and sends a {@link PlotChangeEvent} to all
1834         * registered listeners.
1835         *
1836         * @param stroke  the stroke.
1837         *
1838         * @see #getLabelLinkStroke()
1839         */
1840        public void setLabelLinkStroke(Stroke stroke) {
1841            if (stroke == null) {
1842                throw new IllegalArgumentException("Null 'stroke' argument.");
1843            }
1844            this.labelLinkStroke = stroke;
1845            fireChangeEvent();
1846        }
1847    
1848        /**
1849         * Returns the section label font.
1850         *
1851         * @return The font (never <code>null</code>).
1852         *
1853         * @see #setLabelFont(Font)
1854         */
1855        public Font getLabelFont() {
1856            return this.labelFont;
1857        }
1858    
1859        /**
1860         * Sets the section label font and sends a {@link PlotChangeEvent} to all
1861         * registered listeners.
1862         *
1863         * @param font  the font (<code>null</code> not permitted).
1864         *
1865         * @see #getLabelFont()
1866         */
1867        public void setLabelFont(Font font) {
1868            if (font == null) {
1869                throw new IllegalArgumentException("Null 'font' argument.");
1870            }
1871            this.labelFont = font;
1872            fireChangeEvent();
1873        }
1874    
1875        /**
1876         * Returns the section label paint.
1877         *
1878         * @return The paint (never <code>null</code>).
1879         *
1880         * @see #setLabelPaint(Paint)
1881         */
1882        public Paint getLabelPaint() {
1883            return this.labelPaint;
1884        }
1885    
1886        /**
1887         * Sets the section label paint and sends a {@link PlotChangeEvent} to all
1888         * registered listeners.
1889         *
1890         * @param paint  the paint (<code>null</code> not permitted).
1891         *
1892         * @see #getLabelPaint()
1893         */
1894        public void setLabelPaint(Paint paint) {
1895            if (paint == null) {
1896                throw new IllegalArgumentException("Null 'paint' argument.");
1897            }
1898            this.labelPaint = paint;
1899            fireChangeEvent();
1900        }
1901    
1902        /**
1903         * Returns the section label background paint.
1904         *
1905         * @return The paint (possibly <code>null</code>).
1906         *
1907         * @see #setLabelBackgroundPaint(Paint)
1908         */
1909        public Paint getLabelBackgroundPaint() {
1910            return this.labelBackgroundPaint;
1911        }
1912    
1913        /**
1914         * Sets the section label background paint and sends a
1915         * {@link PlotChangeEvent} to all registered listeners.
1916         *
1917         * @param paint  the paint (<code>null</code> permitted).
1918         *
1919         * @see #getLabelBackgroundPaint()
1920         */
1921        public void setLabelBackgroundPaint(Paint paint) {
1922            this.labelBackgroundPaint = paint;
1923            fireChangeEvent();
1924        }
1925    
1926        /**
1927         * Returns the section label outline paint.
1928         *
1929         * @return The paint (possibly <code>null</code>).
1930         *
1931         * @see #setLabelOutlinePaint(Paint)
1932         */
1933        public Paint getLabelOutlinePaint() {
1934            return this.labelOutlinePaint;
1935        }
1936    
1937        /**
1938         * Sets the section label outline paint and sends a
1939         * {@link PlotChangeEvent} to all registered listeners.
1940         *
1941         * @param paint  the paint (<code>null</code> permitted).
1942         *
1943         * @see #getLabelOutlinePaint()
1944         */
1945        public void setLabelOutlinePaint(Paint paint) {
1946            this.labelOutlinePaint = paint;
1947            fireChangeEvent();
1948        }
1949    
1950        /**
1951         * Returns the section label outline stroke.
1952         *
1953         * @return The stroke (possibly <code>null</code>).
1954         *
1955         * @see #setLabelOutlineStroke(Stroke)
1956         */
1957        public Stroke getLabelOutlineStroke() {
1958            return this.labelOutlineStroke;
1959        }
1960    
1961        /**
1962         * Sets the section label outline stroke and sends a
1963         * {@link PlotChangeEvent} to all registered listeners.
1964         *
1965         * @param stroke  the stroke (<code>null</code> permitted).
1966         *
1967         * @see #getLabelOutlineStroke()
1968         */
1969        public void setLabelOutlineStroke(Stroke stroke) {
1970            this.labelOutlineStroke = stroke;
1971            fireChangeEvent();
1972        }
1973    
1974        /**
1975         * Returns the section label shadow paint.
1976         *
1977         * @return The paint (possibly <code>null</code>).
1978         *
1979         * @see #setLabelShadowPaint(Paint)
1980         */
1981        public Paint getLabelShadowPaint() {
1982            return this.labelShadowPaint;
1983        }
1984    
1985        /**
1986         * Sets the section label shadow paint and sends a {@link PlotChangeEvent}
1987         * to all registered listeners.
1988         *
1989         * @param paint  the paint (<code>null</code> permitted).
1990         *
1991         * @see #getLabelShadowPaint()
1992         */
1993        public void setLabelShadowPaint(Paint paint) {
1994            this.labelShadowPaint = paint;
1995            fireChangeEvent();
1996        }
1997    
1998        /**
1999         * Returns the label padding.
2000         *
2001         * @return The label padding (never <code>null</code>).
2002         *
2003         * @since 1.0.7
2004         *
2005         * @see #setLabelPadding(RectangleInsets)
2006         */
2007        public RectangleInsets getLabelPadding() {
2008            return this.labelPadding;
2009        }
2010    
2011        /**
2012         * Sets the padding between each label and its outline and sends a
2013         * {@link PlotChangeEvent} to all registered listeners.
2014         *
2015         * @param padding  the padding (<code>null</code> not permitted).
2016         *
2017         * @since 1.0.7
2018         *
2019         * @see #getLabelPadding()
2020         */
2021        public void setLabelPadding(RectangleInsets padding) {
2022            if (padding == null) {
2023                throw new IllegalArgumentException("Null 'padding' argument.");
2024            }
2025            this.labelPadding = padding;
2026            fireChangeEvent();
2027        }
2028    
2029        /**
2030         * Returns the flag that controls whether simple or extended labels are
2031         * displayed on the plot.
2032         *
2033         * @return A boolean.
2034         *
2035         * @since 1.0.7
2036         */
2037        public boolean getSimpleLabels() {
2038            return this.simpleLabels;
2039        }
2040    
2041        /**
2042         * Sets the flag that controls whether simple or extended labels are
2043         * displayed on the plot, and sends a {@link PlotChangeEvent} to all
2044         * registered listeners.
2045         *
2046         * @param simple  the new flag value.
2047         *
2048         * @since 1.0.7
2049         */
2050        public void setSimpleLabels(boolean simple) {
2051            this.simpleLabels = simple;
2052            fireChangeEvent();
2053        }
2054    
2055        /**
2056         * Returns the offset used for the simple labels, if they are displayed.
2057         *
2058         * @return The offset (never <code>null</code>).
2059         *
2060         * @since 1.0.7
2061         *
2062         * @see #setSimpleLabelOffset(RectangleInsets)
2063         */
2064        public RectangleInsets getSimpleLabelOffset() {
2065            return this.simpleLabelOffset;
2066        }
2067    
2068        /**
2069         * Sets the offset for the simple labels and sends a
2070         * {@link PlotChangeEvent} to all registered listeners.
2071         *
2072         * @param offset  the offset (<code>null</code> not permitted).
2073         *
2074         * @since 1.0.7
2075         *
2076         * @see #getSimpleLabelOffset()
2077         */
2078        public void setSimpleLabelOffset(RectangleInsets offset) {
2079            if (offset == null) {
2080                throw new IllegalArgumentException("Null 'offset' argument.");
2081            }
2082            this.simpleLabelOffset = offset;
2083            fireChangeEvent();
2084        }
2085    
2086        /**
2087         * Returns the object responsible for the vertical layout of the pie
2088         * section labels.
2089         *
2090         * @return The label distributor (never <code>null</code>).
2091         *
2092         * @since 1.0.6
2093         */
2094        public AbstractPieLabelDistributor getLabelDistributor() {
2095            return this.labelDistributor;
2096        }
2097    
2098        /**
2099         * Sets the label distributor and sends a {@link PlotChangeEvent} to all
2100         * registered listeners.
2101         *
2102         * @param distributor  the distributor (<code>null</code> not permitted).
2103         *
2104         * @since 1.0.6
2105         */
2106        public void setLabelDistributor(AbstractPieLabelDistributor distributor) {
2107            if (distributor == null) {
2108                throw new IllegalArgumentException("Null 'distributor' argument.");
2109            }
2110            this.labelDistributor = distributor;
2111            fireChangeEvent();
2112        }
2113    
2114        /**
2115         * Returns the tool tip generator, an object that is responsible for
2116         * generating the text items used for tool tips by the plot.  If the
2117         * generator is <code>null</code>, no tool tips will be created.
2118         *
2119         * @return The generator (possibly <code>null</code>).
2120         *
2121         * @see #setToolTipGenerator(PieToolTipGenerator)
2122         */
2123        public PieToolTipGenerator getToolTipGenerator() {
2124            return this.toolTipGenerator;
2125        }
2126    
2127        /**
2128         * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all
2129         * registered listeners.  Set the generator to <code>null</code> if you
2130         * don't want any tool tips.
2131         *
2132         * @param generator  the generator (<code>null</code> permitted).
2133         *
2134         * @see #getToolTipGenerator()
2135         */
2136        public void setToolTipGenerator(PieToolTipGenerator generator) {
2137            this.toolTipGenerator = generator;
2138            fireChangeEvent();
2139        }
2140    
2141        /**
2142         * Returns the URL generator.
2143         *
2144         * @return The generator (possibly <code>null</code>).
2145         *
2146         * @see #setURLGenerator(PieURLGenerator)
2147         */
2148        public PieURLGenerator getURLGenerator() {
2149            return this.urlGenerator;
2150        }
2151    
2152        /**
2153         * Sets the URL generator and sends a {@link PlotChangeEvent} to all
2154         * registered listeners.
2155         *
2156         * @param generator  the generator (<code>null</code> permitted).
2157         *
2158         * @see #getURLGenerator()
2159         */
2160        public void setURLGenerator(PieURLGenerator generator) {
2161            this.urlGenerator = generator;
2162            fireChangeEvent();
2163        }
2164    
2165        /**
2166         * Returns the minimum arc angle that will be drawn.  Pie sections for an
2167         * angle smaller than this are not drawn, to avoid a JDK bug.
2168         *
2169         * @return The minimum angle.
2170         *
2171         * @see #setMinimumArcAngleToDraw(double)
2172         */
2173        public double getMinimumArcAngleToDraw() {
2174            return this.minimumArcAngleToDraw;
2175        }
2176    
2177        /**
2178         * Sets the minimum arc angle that will be drawn.  Pie sections for an
2179         * angle smaller than this are not drawn, to avoid a JDK bug.  See this
2180         * link for details:
2181         * <br><br>
2182         * <a href="http://www.jfree.org/phpBB2/viewtopic.php?t=2707">
2183         * http://www.jfree.org/phpBB2/viewtopic.php?t=2707</a>
2184         * <br><br>
2185         * ...and this bug report in the Java Bug Parade:
2186         * <br><br>
2187         * <a href=
2188         * "http://developer.java.sun.com/developer/bugParade/bugs/4836495.html">
2189         * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html</a>
2190         *
2191         * @param angle  the minimum angle.
2192         *
2193         * @see #getMinimumArcAngleToDraw()
2194         */
2195        public void setMinimumArcAngleToDraw(double angle) {
2196            this.minimumArcAngleToDraw = angle;
2197        }
2198    
2199        /**
2200         * Returns the shape used for legend items.
2201         *
2202         * @return The shape (never <code>null</code>).
2203         *
2204         * @see #setLegendItemShape(Shape)
2205         */
2206        public Shape getLegendItemShape() {
2207            return this.legendItemShape;
2208        }
2209    
2210        /**
2211         * Sets the shape used for legend items and sends a {@link PlotChangeEvent}
2212         * to all registered listeners.
2213         *
2214         * @param shape  the shape (<code>null</code> not permitted).
2215         *
2216         * @see #getLegendItemShape()
2217         */
2218        public void setLegendItemShape(Shape shape) {
2219            if (shape == null) {
2220                throw new IllegalArgumentException("Null 'shape' argument.");
2221            }
2222            this.legendItemShape = shape;
2223            fireChangeEvent();
2224        }
2225    
2226        /**
2227         * Returns the legend label generator.
2228         *
2229         * @return The legend label generator (never <code>null</code>).
2230         *
2231         * @see #setLegendLabelGenerator(PieSectionLabelGenerator)
2232         */
2233        public PieSectionLabelGenerator getLegendLabelGenerator() {
2234            return this.legendLabelGenerator;
2235        }
2236    
2237        /**
2238         * Sets the legend label generator and sends a {@link PlotChangeEvent} to
2239         * all registered listeners.
2240         *
2241         * @param generator  the generator (<code>null</code> not permitted).
2242         *
2243         * @see #getLegendLabelGenerator()
2244         */
2245        public void setLegendLabelGenerator(PieSectionLabelGenerator generator) {
2246            if (generator == null) {
2247                throw new IllegalArgumentException("Null 'generator' argument.");
2248            }
2249            this.legendLabelGenerator = generator;
2250            fireChangeEvent();
2251        }
2252    
2253        /**
2254         * Returns the legend label tool tip generator.
2255         *
2256         * @return The legend label tool tip generator (possibly <code>null</code>).
2257         *
2258         * @see #setLegendLabelToolTipGenerator(PieSectionLabelGenerator)
2259         */
2260        public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
2261            return this.legendLabelToolTipGenerator;
2262        }
2263    
2264        /**
2265         * Sets the legend label tool tip generator and sends a
2266         * {@link PlotChangeEvent} to all registered listeners.
2267         *
2268         * @param generator  the generator (<code>null</code> permitted).
2269         *
2270         * @see #getLegendLabelToolTipGenerator()
2271         */
2272        public void setLegendLabelToolTipGenerator(
2273                PieSectionLabelGenerator generator) {
2274            this.legendLabelToolTipGenerator = generator;
2275            fireChangeEvent();
2276        }
2277    
2278        /**
2279         * Returns the legend label URL generator.
2280         *
2281         * @return The legend label URL generator (possibly <code>null</code>).
2282         *
2283         * @see #setLegendLabelURLGenerator(PieURLGenerator)
2284         *
2285         * @since 1.0.4
2286         */
2287        public PieURLGenerator getLegendLabelURLGenerator() {
2288            return this.legendLabelURLGenerator;
2289        }
2290    
2291        /**
2292         * Sets the legend label URL generator and sends a
2293         * {@link PlotChangeEvent} to all registered listeners.
2294         *
2295         * @param generator  the generator (<code>null</code> permitted).
2296         *
2297         * @see #getLegendLabelURLGenerator()
2298         *
2299         * @since 1.0.4
2300         */
2301        public void setLegendLabelURLGenerator(PieURLGenerator generator) {
2302            this.legendLabelURLGenerator = generator;
2303            fireChangeEvent();
2304        }
2305    
2306        /**
2307         * Initialises the drawing procedure.  This method will be called before
2308         * the first item is rendered, giving the plot an opportunity to initialise
2309         * any state information it wants to maintain.
2310         *
2311         * @param g2  the graphics device.
2312         * @param plotArea  the plot area (<code>null</code> not permitted).
2313         * @param plot  the plot.
2314         * @param index  the secondary index (<code>null</code> for primary
2315         *               renderer).
2316         * @param info  collects chart rendering information for return to caller.
2317         *
2318         * @return A state object (maintains state information relevant to one
2319         *         chart drawing).
2320         */
2321        public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea,
2322                PiePlot plot, Integer index, PlotRenderingInfo info) {
2323    
2324            PiePlotState state = new PiePlotState(info);
2325            state.setPassesRequired(2);
2326            if (this.dataset != null) {
2327                state.setTotal(DatasetUtilities.calculatePieDatasetTotal(
2328                        plot.getDataset()));
2329            }
2330            state.setLatestAngle(plot.getStartAngle());
2331            return state;
2332    
2333        }
2334    
2335        /**
2336         * Draws the plot on a Java 2D graphics device (such as the screen or a
2337         * printer).
2338         *
2339         * @param g2  the graphics device.
2340         * @param area  the area within which the plot should be drawn.
2341         * @param anchor  the anchor point (<code>null</code> permitted).
2342         * @param parentState  the state from the parent plot, if there is one.
2343         * @param info  collects info about the drawing
2344         *              (<code>null</code> permitted).
2345         */
2346        public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2347                         PlotState parentState, PlotRenderingInfo info) {
2348    
2349            // adjust for insets...
2350            RectangleInsets insets = getInsets();
2351            insets.trim(area);
2352    
2353            if (info != null) {
2354                info.setPlotArea(area);
2355                info.setDataArea(area);
2356            }
2357    
2358            drawBackground(g2, area);
2359            drawOutline(g2, area);
2360    
2361            Shape savedClip = g2.getClip();
2362            g2.clip(area);
2363    
2364            Composite originalComposite = g2.getComposite();
2365            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2366                    getForegroundAlpha()));
2367    
2368            if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
2369                drawPie(g2, area, info);
2370            }
2371            else {
2372                drawNoDataMessage(g2, area);
2373            }
2374    
2375            g2.setClip(savedClip);
2376            g2.setComposite(originalComposite);
2377    
2378            drawOutline(g2, area);
2379    
2380        }
2381    
2382        /**
2383         * Draws the pie.
2384         *
2385         * @param g2  the graphics device.
2386         * @param plotArea  the plot area.
2387         * @param info  chart rendering info.
2388         */
2389        protected void drawPie(Graphics2D g2, Rectangle2D plotArea,
2390                               PlotRenderingInfo info) {
2391    
2392            PiePlotState state = initialise(g2, plotArea, this, null, info);
2393    
2394            // adjust the plot area for interior spacing and labels...
2395            double labelReserve = 0.0;
2396            if (this.labelGenerator != null && !this.simpleLabels) {
2397                labelReserve = this.labelGap + this.maximumLabelWidth;
2398            }
2399            double gapHorizontal = plotArea.getWidth() * (this.interiorGap
2400                    + labelReserve) * 2.0;
2401            double gapVertical = plotArea.getHeight() * this.interiorGap * 2.0;
2402    
2403    
2404            if (DEBUG_DRAW_INTERIOR) {
2405                double hGap = plotArea.getWidth() * this.interiorGap;
2406                double vGap = plotArea.getHeight() * this.interiorGap;
2407    
2408                double igx1 = plotArea.getX() + hGap;
2409                double igx2 = plotArea.getMaxX() - hGap;
2410                double igy1 = plotArea.getY() + vGap;
2411                double igy2 = plotArea.getMaxY() - vGap;
2412                g2.setPaint(Color.gray);
2413                g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1,
2414                        igy2 - igy1));
2415            }
2416    
2417            double linkX = plotArea.getX() + gapHorizontal / 2;
2418            double linkY = plotArea.getY() + gapVertical / 2;
2419            double linkW = plotArea.getWidth() - gapHorizontal;
2420            double linkH = plotArea.getHeight() - gapVertical;
2421    
2422            // make the link area a square if the pie chart is to be circular...
2423            if (this.circular) {
2424                double min = Math.min(linkW, linkH) / 2;
2425                linkX = (linkX + linkX + linkW) / 2 - min;
2426                linkY = (linkY + linkY + linkH) / 2 - min;
2427                linkW = 2 * min;
2428                linkH = 2 * min;
2429            }
2430    
2431            // the link area defines the dog leg points for the linking lines to
2432            // the labels
2433            Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW,
2434                    linkH);
2435            state.setLinkArea(linkArea);
2436    
2437            if (DEBUG_DRAW_LINK_AREA) {
2438                g2.setPaint(Color.blue);
2439                g2.draw(linkArea);
2440                g2.setPaint(Color.yellow);
2441                g2.draw(new Ellipse2D.Double(linkArea.getX(), linkArea.getY(),
2442                        linkArea.getWidth(), linkArea.getHeight()));
2443            }
2444    
2445            // the explode area defines the max circle/ellipse for the exploded
2446            // pie sections.  it is defined by shrinking the linkArea by the
2447            // linkMargin factor.
2448            double lm = 0.0;
2449            if (!this.simpleLabels) {
2450                lm = this.labelLinkMargin;
2451            }
2452            double hh = linkArea.getWidth() * lm * 2.0;
2453            double vv = linkArea.getHeight() * lm * 2.0;
2454            Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0,
2455                    linkY + vv / 2.0, linkW - hh, linkH - vv);
2456    
2457            state.setExplodedPieArea(explodeArea);
2458    
2459            // the pie area defines the circle/ellipse for regular pie sections.
2460            // it is defined by shrinking the explodeArea by the explodeMargin
2461            // factor.
2462            double maximumExplodePercent = getMaximumExplodePercent();
2463            double percent = maximumExplodePercent / (1.0 + maximumExplodePercent);
2464    
2465            double h1 = explodeArea.getWidth() * percent;
2466            double v1 = explodeArea.getHeight() * percent;
2467            Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX()
2468                    + h1 / 2.0, explodeArea.getY() + v1 / 2.0,
2469                    explodeArea.getWidth() - h1, explodeArea.getHeight() - v1);
2470    
2471            if (DEBUG_DRAW_PIE_AREA) {
2472                g2.setPaint(Color.green);
2473                g2.draw(pieArea);
2474            }
2475            state.setPieArea(pieArea);
2476            state.setPieCenterX(pieArea.getCenterX());
2477            state.setPieCenterY(pieArea.getCenterY());
2478            state.setPieWRadius(pieArea.getWidth() / 2.0);
2479            state.setPieHRadius(pieArea.getHeight() / 2.0);
2480    
2481            // plot the data (unless the dataset is null)...
2482            if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) {
2483    
2484                List keys = this.dataset.getKeys();
2485                double totalValue = DatasetUtilities.calculatePieDatasetTotal(
2486                        this.dataset);
2487    
2488                int passesRequired = state.getPassesRequired();
2489                for (int pass = 0; pass < passesRequired; pass++) {
2490                    double runningTotal = 0.0;
2491                    for (int section = 0; section < keys.size(); section++) {
2492                        Number n = this.dataset.getValue(section);
2493                        if (n != null) {
2494                            double value = n.doubleValue();
2495                            if (value > 0.0) {
2496                                runningTotal += value;
2497                                drawItem(g2, section, explodeArea, state, pass);
2498                            }
2499                        }
2500                    }
2501                }
2502                if (this.simpleLabels) {
2503                    drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea,
2504                            state);
2505                }
2506                else {
2507                    drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
2508                }
2509    
2510            }
2511            else {
2512                drawNoDataMessage(g2, plotArea);
2513            }
2514        }
2515    
2516        /**
2517         * Draws a single data item.
2518         *
2519         * @param g2  the graphics device (<code>null</code> not permitted).
2520         * @param section  the section index.
2521         * @param dataArea  the data plot area.
2522         * @param state  state information for one chart.
2523         * @param currentPass  the current pass index.
2524         */
2525        protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea,
2526                                PiePlotState state, int currentPass) {
2527    
2528            Number n = this.dataset.getValue(section);
2529            if (n == null) {
2530                return;
2531            }
2532            double value = n.doubleValue();
2533            double angle1 = 0.0;
2534            double angle2 = 0.0;
2535    
2536            if (this.direction == Rotation.CLOCKWISE) {
2537                angle1 = state.getLatestAngle();
2538                angle2 = angle1 - value / state.getTotal() * 360.0;
2539            }
2540            else if (this.direction == Rotation.ANTICLOCKWISE) {
2541                angle1 = state.getLatestAngle();
2542                angle2 = angle1 + value / state.getTotal() * 360.0;
2543            }
2544            else {
2545                throw new IllegalStateException("Rotation type not recognised.");
2546            }
2547    
2548            double angle = (angle2 - angle1);
2549            if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
2550                double ep = 0.0;
2551                double mep = getMaximumExplodePercent();
2552                if (mep > 0.0) {
2553                    ep = getExplodePercent(section) / mep;
2554                }
2555                Rectangle2D arcBounds = getArcBounds(state.getPieArea(),
2556                        state.getExplodedPieArea(), angle1, angle, ep);
2557                Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle,
2558                        Arc2D.PIE);
2559    
2560                if (currentPass == 0) {
2561                    if (this.shadowPaint != null) {
2562                        Shape shadowArc = ShapeUtilities.createTranslatedShape(
2563                                arc, (float) this.shadowXOffset,
2564                                (float) this.shadowYOffset);
2565                        g2.setPaint(this.shadowPaint);
2566                        g2.fill(shadowArc);
2567                    }
2568                }
2569                else if (currentPass == 1) {
2570                    Comparable key = getSectionKey(section);
2571                    Paint paint = lookupSectionPaint(key);
2572                    g2.setPaint(paint);
2573                    g2.fill(arc);
2574    
2575                    Paint outlinePaint = lookupSectionOutlinePaint(key);
2576                    Stroke outlineStroke = lookupSectionOutlineStroke(key);
2577                    if (this.sectionOutlinesVisible) {
2578                        g2.setPaint(outlinePaint);
2579                        g2.setStroke(outlineStroke);
2580                        g2.draw(arc);
2581                    }
2582    
2583                    // update the linking line target for later
2584                    // add an entity for the pie section
2585                    if (state.getInfo() != null) {
2586                        EntityCollection entities = state.getEntityCollection();
2587                        if (entities != null) {
2588                            String tip = null;
2589                            if (this.toolTipGenerator != null) {
2590                                tip = this.toolTipGenerator.generateToolTip(
2591                                        this.dataset, key);
2592                            }
2593                            String url = null;
2594                            if (this.urlGenerator != null) {
2595                                url = this.urlGenerator.generateURL(this.dataset,
2596                                        key, this.pieIndex);
2597                            }
2598                            PieSectionEntity entity = new PieSectionEntity(
2599                                    arc, this.dataset, this.pieIndex, section, key,
2600                                    tip, url);
2601                            entities.add(entity);
2602                        }
2603                    }
2604                }
2605            }
2606            state.setLatestAngle(angle2);
2607        }
2608    
2609        /**
2610         * Draws the pie section labels in the simple form.
2611         *
2612         * @param g2  the graphics device.
2613         * @param keys  the section keys.
2614         * @param totalValue  the total value for all sections in the pie.
2615         * @param plotArea  the plot area.
2616         * @param pieArea  the area containing the pie.
2617         * @param state  the plot state.
2618         *
2619         * @since 1.0.7
2620         */
2621        protected void drawSimpleLabels(Graphics2D g2, List keys,
2622                double totalValue, Rectangle2D plotArea, Rectangle2D pieArea,
2623                PiePlotState state) {
2624    
2625            Composite originalComposite = g2.getComposite();
2626            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2627                    1.0f));
2628    
2629            RectangleInsets labelInsets = new RectangleInsets(UnitType.RELATIVE,
2630                    0.18, 0.18, 0.18, 0.18);
2631            Rectangle2D labelsArea = labelInsets.createInsetRectangle(pieArea);
2632            double runningTotal = 0.0;
2633            Iterator iterator = keys.iterator();
2634            while (iterator.hasNext()) {
2635                Comparable key = (Comparable) iterator.next();
2636                boolean include = true;
2637                double v = 0.0;
2638                Number n = getDataset().getValue(key);
2639                if (n == null) {
2640                    include = !getIgnoreNullValues();
2641                }
2642                else {
2643                    v = n.doubleValue();
2644                    include = getIgnoreZeroValues() ? v > 0.0 : v >= 0.0;
2645                }
2646    
2647                if (include) {
2648                    runningTotal = runningTotal + v;
2649                    // work out the mid angle (0 - 90 and 270 - 360) = right,
2650                    // otherwise left
2651                    double mid = getStartAngle() + (getDirection().getFactor()
2652                            * ((runningTotal - v / 2.0) * 360) / totalValue);
2653    
2654                    Arc2D arc = new Arc2D.Double(labelsArea, getStartAngle(),
2655                            mid - getStartAngle(), Arc2D.OPEN);
2656                    int x = (int) arc.getEndPoint().getX();
2657                    int y = (int) arc.getEndPoint().getY();
2658    
2659                    PieSectionLabelGenerator labelGenerator = getLabelGenerator();
2660                    if (labelGenerator == null) {
2661                        continue;
2662                    }
2663                    String label = labelGenerator.generateSectionLabel(
2664                            this.dataset, key);
2665                    if (label == null) {
2666                        continue;
2667                    }
2668                    g2.setFont(this.labelFont);
2669                    FontMetrics fm = g2.getFontMetrics();
2670                    Rectangle2D bounds = TextUtilities.getTextBounds(label, g2, fm);
2671                    Rectangle2D out = this.labelPadding.createOutsetRectangle(
2672                            bounds);
2673                    Shape bg = ShapeUtilities.createTranslatedShape(out,
2674                            x - bounds.getCenterX(), y - bounds.getCenterY());
2675                    if (this.labelShadowPaint != null) {
2676                        Shape shadow = ShapeUtilities.createTranslatedShape(bg,
2677                                this.shadowXOffset, this.shadowYOffset);
2678                        g2.setPaint(this.labelShadowPaint);
2679                        g2.fill(shadow);
2680                    }
2681                    if (this.labelBackgroundPaint != null) {
2682                        g2.setPaint(this.labelBackgroundPaint);
2683                        g2.fill(bg);
2684                    }
2685                    if (this.labelOutlinePaint != null
2686                            && this.labelOutlineStroke != null) {
2687                        g2.setPaint(this.labelOutlinePaint);
2688                        g2.setStroke(this.labelOutlineStroke);
2689                        g2.draw(bg);
2690                    }
2691    
2692                    g2.setPaint(this.labelPaint);
2693                    g2.setFont(this.labelFont);
2694                    TextUtilities.drawAlignedString(getLabelGenerator()
2695                            .generateSectionLabel(getDataset(), key), g2, x, y,
2696                            TextAnchor.CENTER);
2697    
2698                }
2699            }
2700    
2701            g2.setComposite(originalComposite);
2702    
2703        }
2704    
2705        /**
2706         * Draws the labels for the pie sections.
2707         *
2708         * @param g2  the graphics device.
2709         * @param keys  the keys.
2710         * @param totalValue  the total value.
2711         * @param plotArea  the plot area.
2712         * @param linkArea  the link area.
2713         * @param state  the state.
2714         */
2715        protected void drawLabels(Graphics2D g2, List keys, double totalValue,
2716                                  Rectangle2D plotArea, Rectangle2D linkArea,
2717                                  PiePlotState state) {
2718    
2719            Composite originalComposite = g2.getComposite();
2720            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2721                    1.0f));
2722    
2723            // classify the keys according to which side the label will appear...
2724            DefaultKeyedValues leftKeys = new DefaultKeyedValues();
2725            DefaultKeyedValues rightKeys = new DefaultKeyedValues();
2726    
2727            double runningTotal = 0.0;
2728            Iterator iterator = keys.iterator();
2729            while (iterator.hasNext()) {
2730                Comparable key = (Comparable) iterator.next();
2731                boolean include = true;
2732                double v = 0.0;
2733                Number n = this.dataset.getValue(key);
2734                if (n == null) {
2735                    include = !this.ignoreNullValues;
2736                }
2737                else {
2738                    v = n.doubleValue();
2739                    include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0;
2740                }
2741    
2742                if (include) {
2743                    runningTotal = runningTotal + v;
2744                    // work out the mid angle (0 - 90 and 270 - 360) = right,
2745                    // otherwise left
2746                    double mid = this.startAngle + (this.direction.getFactor()
2747                            * ((runningTotal - v / 2.0) * 360) / totalValue);
2748                    if (Math.cos(Math.toRadians(mid)) < 0.0) {
2749                        leftKeys.addValue(key, new Double(mid));
2750                    }
2751                    else {
2752                        rightKeys.addValue(key, new Double(mid));
2753                    }
2754                }
2755            }
2756    
2757            g2.setFont(getLabelFont());
2758    
2759            // calculate the max label width from the plot dimensions, because
2760            // a circular pie can leave a lot more room for labels...
2761            double marginX = plotArea.getX() + this.interiorGap
2762                    * plotArea.getWidth();
2763            double gap = plotArea.getWidth() * this.labelGap;
2764            double ww = linkArea.getX() - gap - marginX;
2765            float labelWidth = (float) this.labelPadding.trimWidth(ww);
2766    
2767            // draw the labels...
2768            if (this.labelGenerator != null) {
2769                drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth,
2770                        state);
2771                drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth,
2772                        state);
2773            }
2774            g2.setComposite(originalComposite);
2775    
2776        }
2777    
2778        /**
2779         * Draws the left labels.
2780         *
2781         * @param leftKeys  a collection of keys and angles (to the middle of the
2782         *         section, in degrees) for the sections on the left side of the
2783         *         plot.
2784         * @param g2  the graphics device.
2785         * @param plotArea  the plot area.
2786         * @param linkArea  the link area.
2787         * @param maxLabelWidth  the maximum label width.
2788         * @param state  the state.
2789         */
2790        protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2,
2791                                      Rectangle2D plotArea, Rectangle2D linkArea,
2792                                      float maxLabelWidth, PiePlotState state) {
2793    
2794            this.labelDistributor.clear();
2795            double lGap = plotArea.getWidth() * this.labelGap;
2796            double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2797            for (int i = 0; i < leftKeys.getItemCount(); i++) {
2798                String label = this.labelGenerator.generateSectionLabel(
2799                        this.dataset, leftKeys.getKey(i));
2800                if (label != null) {
2801                    TextBlock block = TextUtilities.createTextBlock(label,
2802                            this.labelFont, this.labelPaint, maxLabelWidth,
2803                            new G2TextMeasurer(g2));
2804                    TextBox labelBox = new TextBox(block);
2805                    labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2806                    labelBox.setOutlinePaint(this.labelOutlinePaint);
2807                    labelBox.setOutlineStroke(this.labelOutlineStroke);
2808                    labelBox.setShadowPaint(this.labelShadowPaint);
2809                    labelBox.setInteriorGap(this.labelPadding);
2810                    double theta = Math.toRadians(
2811                            leftKeys.getValue(i).doubleValue());
2812                    double baseY = state.getPieCenterY() - Math.sin(theta)
2813                                   * verticalLinkRadius;
2814                    double hh = labelBox.getHeight(g2);
2815    
2816                    this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2817                            leftKeys.getKey(i), theta, baseY, labelBox, hh,
2818                            lGap / 2.0 + lGap / 2.0 * -Math.cos(theta), 0.9
2819                            + getExplodePercent(leftKeys.getKey(i))));
2820                }
2821            }
2822            double hh = plotArea.getHeight();
2823            double gap = hh * getInteriorGap();
2824            this.labelDistributor.distributeLabels(plotArea.getMinY() + gap,
2825                    hh - 2 * gap);
2826            for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2827                drawLeftLabel(g2, state,
2828                        this.labelDistributor.getPieLabelRecord(i));
2829            }
2830        }
2831    
2832        /**
2833         * Draws the right labels.
2834         *
2835         * @param keys  the keys.
2836         * @param g2  the graphics device.
2837         * @param plotArea  the plot area.
2838         * @param linkArea  the link area.
2839         * @param maxLabelWidth  the maximum label width.
2840         * @param state  the state.
2841         */
2842        protected void drawRightLabels(KeyedValues keys, Graphics2D g2,
2843                                       Rectangle2D plotArea, Rectangle2D linkArea,
2844                                       float maxLabelWidth, PiePlotState state) {
2845    
2846            // draw the right labels...
2847            this.labelDistributor.clear();
2848            double lGap = plotArea.getWidth() * this.labelGap;
2849            double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2850    
2851            for (int i = 0; i < keys.getItemCount(); i++) {
2852                String label = this.labelGenerator.generateSectionLabel(
2853                        this.dataset, keys.getKey(i));
2854    
2855                if (label != null) {
2856                    TextBlock block = TextUtilities.createTextBlock(label,
2857                            this.labelFont, this.labelPaint, maxLabelWidth,
2858                            new G2TextMeasurer(g2));
2859                    TextBox labelBox = new TextBox(block);
2860                    labelBox.setBackgroundPaint(this.labelBackgroundPaint);
2861                    labelBox.setOutlinePaint(this.labelOutlinePaint);
2862                    labelBox.setOutlineStroke(this.labelOutlineStroke);
2863                    labelBox.setShadowPaint(this.labelShadowPaint);
2864                    labelBox.setInteriorGap(this.labelPadding);
2865                    double theta = Math.toRadians(keys.getValue(i).doubleValue());
2866                    double baseY = state.getPieCenterY()
2867                                  - Math.sin(theta) * verticalLinkRadius;
2868                    double hh = labelBox.getHeight(g2);
2869                    this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
2870                            keys.getKey(i), theta, baseY, labelBox, hh,
2871                            lGap / 2.0 + lGap / 2.0 * Math.cos(theta),
2872                            0.9 + getExplodePercent(keys.getKey(i))));
2873                }
2874            }
2875            double hh = plotArea.getHeight();
2876            double gap = hh * getInteriorGap();
2877            this.labelDistributor.distributeLabels(plotArea.getMinY() + gap,
2878                    hh - 2 * gap);
2879            for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
2880                drawRightLabel(g2, state,
2881                        this.labelDistributor.getPieLabelRecord(i));
2882            }
2883    
2884        }
2885    
2886        /**
2887         * Returns a collection of legend items for the pie chart.
2888         *
2889         * @return The legend items (never <code>null</code>).
2890         */
2891        public LegendItemCollection getLegendItems() {
2892    
2893            LegendItemCollection result = new LegendItemCollection();
2894            if (this.dataset == null) {
2895                return result;
2896            }
2897            List keys = this.dataset.getKeys();
2898            int section = 0;
2899            Shape shape = getLegendItemShape();
2900            Iterator iterator = keys.iterator();
2901            while (iterator.hasNext()) {
2902                Comparable key = (Comparable) iterator.next();
2903                Number n = this.dataset.getValue(key);
2904                boolean include = true;
2905                if (n == null) {
2906                    include = !this.ignoreNullValues;
2907                }
2908                else {
2909                    double v = n.doubleValue();
2910                    if (v == 0.0) {
2911                        include = !this.ignoreZeroValues;
2912                    }
2913                    else {
2914                        include = v > 0.0;
2915                    }
2916                }
2917                if (include) {
2918                    String label = this.legendLabelGenerator.generateSectionLabel(
2919                            this.dataset, key);
2920                    if (label != null) {
2921                        String description = label;
2922                        String toolTipText = null;
2923                        if (this.legendLabelToolTipGenerator != null) {
2924                            toolTipText = this.legendLabelToolTipGenerator
2925                                    .generateSectionLabel(this.dataset, key);
2926                        }
2927                        String urlText = null;
2928                        if (this.legendLabelURLGenerator != null) {
2929                            urlText = this.legendLabelURLGenerator.generateURL(
2930                                    this.dataset, key, this.pieIndex);
2931                        }
2932                        Paint paint = lookupSectionPaint(key);
2933                        Paint outlinePaint = lookupSectionOutlinePaint(key);
2934                        Stroke outlineStroke = lookupSectionOutlineStroke(key);
2935                        LegendItem item = new LegendItem(label, description,
2936                                toolTipText, urlText, true, shape, true, paint,
2937                                true, outlinePaint, outlineStroke,
2938                                false,          // line not visible
2939                                new Line2D.Float(), new BasicStroke(), Color.black);
2940                        item.setDataset(getDataset());
2941                        item.setSeriesIndex(this.dataset.getIndex(key));
2942                        item.setSeriesKey(key);
2943                        result.add(item);
2944                    }
2945                    section++;
2946                }
2947                else {
2948                    section++;
2949                }
2950            }
2951            return result;
2952        }
2953    
2954        /**
2955         * Returns a short string describing the type of plot.
2956         *
2957         * @return The plot type.
2958         */
2959        public String getPlotType() {
2960            return localizationResources.getString("Pie_Plot");
2961        }
2962    
2963        /**
2964         * Returns a rectangle that can be used to create a pie section (taking
2965         * into account the amount by which the pie section is 'exploded').
2966         *
2967         * @param unexploded  the area inside which the unexploded pie sections are
2968         *                    drawn.
2969         * @param exploded  the area inside which the exploded pie sections are
2970         *                  drawn.
2971         * @param angle  the start angle.
2972         * @param extent  the extent of the arc.
2973         * @param explodePercent  the amount by which the pie section is exploded.
2974         *
2975         * @return A rectangle that can be used to create a pie section.
2976         */
2977        protected Rectangle2D getArcBounds(Rectangle2D unexploded,
2978                                           Rectangle2D exploded,
2979                                           double angle, double extent,
2980                                           double explodePercent) {
2981    
2982            if (explodePercent == 0.0) {
2983                return unexploded;
2984            }
2985            else {
2986                Arc2D arc1 = new Arc2D.Double(unexploded, angle, extent / 2,
2987                        Arc2D.OPEN);
2988                Point2D point1 = arc1.getEndPoint();
2989                Arc2D.Double arc2 = new Arc2D.Double(exploded, angle, extent / 2,
2990                        Arc2D.OPEN);
2991                Point2D point2 = arc2.getEndPoint();
2992                double deltaX = (point1.getX() - point2.getX()) * explodePercent;
2993                double deltaY = (point1.getY() - point2.getY()) * explodePercent;
2994                return new Rectangle2D.Double(unexploded.getX() - deltaX,
2995                        unexploded.getY() - deltaY, unexploded.getWidth(),
2996                        unexploded.getHeight());
2997            }
2998        }
2999    
3000        /**
3001         * Draws a section label on the left side of the pie chart.
3002         *
3003         * @param g2  the graphics device.
3004         * @param state  the state.
3005         * @param record  the label record.
3006         */
3007        protected void drawLeftLabel(Graphics2D g2, PiePlotState state,
3008                                     PieLabelRecord record) {
3009    
3010            double anchorX = state.getLinkArea().getMinX();
3011            double targetX = anchorX - record.getGap();
3012            double targetY = record.getAllocatedY();
3013    
3014            if (this.labelLinksVisible) {
3015                double theta = record.getAngle();
3016                double linkX = state.getPieCenterX() + Math.cos(theta)
3017                        * state.getPieWRadius() * record.getLinkPercent();
3018                double linkY = state.getPieCenterY() - Math.sin(theta)
3019                        * state.getPieHRadius() * record.getLinkPercent();
3020                double elbowX = state.getPieCenterX() + Math.cos(theta)
3021                        * state.getLinkArea().getWidth() / 2.0;
3022                double elbowY = state.getPieCenterY() - Math.sin(theta)
3023                        * state.getLinkArea().getHeight() / 2.0;
3024                double anchorY = elbowY;
3025                g2.setPaint(this.labelLinkPaint);
3026                g2.setStroke(this.labelLinkStroke);
3027                PieLabelLinkStyle style = getLabelLinkStyle();
3028                if (style.equals(PieLabelLinkStyle.STANDARD)) {
3029                    g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
3030                    g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
3031                    g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
3032                }
3033                else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) {
3034                    QuadCurve2D q = new QuadCurve2D.Float();
3035                    q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY);
3036                    g2.draw(q);
3037                    g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY));
3038                }
3039                else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) {
3040                    CubicCurve2D c = new CubicCurve2D .Float();
3041                    c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY,
3042                            linkX, linkY);
3043                    g2.draw(c);
3044                }
3045            }
3046            TextBox tb = record.getLabel();
3047            tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT);
3048    
3049        }
3050    
3051        /**
3052         * Draws a section label on the right side of the pie chart.
3053         *
3054         * @param g2  the graphics device.
3055         * @param state  the state.
3056         * @param record  the label record.
3057         */
3058        protected void drawRightLabel(Graphics2D g2, PiePlotState state,
3059                                      PieLabelRecord record) {
3060    
3061            double anchorX = state.getLinkArea().getMaxX();
3062            double targetX = anchorX + record.getGap();
3063            double targetY = record.getAllocatedY();
3064    
3065            if (this.labelLinksVisible) {
3066                double theta = record.getAngle();
3067                double linkX = state.getPieCenterX() + Math.cos(theta)
3068                        * state.getPieWRadius() * record.getLinkPercent();
3069                double linkY = state.getPieCenterY() - Math.sin(theta)
3070                        * state.getPieHRadius() * record.getLinkPercent();
3071                double elbowX = state.getPieCenterX() + Math.cos(theta)
3072                        * state.getLinkArea().getWidth() / 2.0;
3073                double elbowY = state.getPieCenterY() - Math.sin(theta)
3074                        * state.getLinkArea().getHeight() / 2.0;
3075                double anchorY = elbowY;
3076                g2.setPaint(this.labelLinkPaint);
3077                g2.setStroke(this.labelLinkStroke);
3078                PieLabelLinkStyle style = getLabelLinkStyle();
3079                if (style.equals(PieLabelLinkStyle.STANDARD)) {
3080                    g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
3081                    g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY));
3082                    g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY));
3083                }
3084                else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) {
3085                    QuadCurve2D q = new QuadCurve2D.Float();
3086                    q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY);
3087                    g2.draw(q);
3088                    g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY));
3089                }
3090                else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) {
3091                    CubicCurve2D c = new CubicCurve2D .Float();
3092                    c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY,
3093                            linkX, linkY);
3094                    g2.draw(c);
3095                }
3096            }
3097    
3098            TextBox tb = record.getLabel();
3099            tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT);
3100    
3101        }
3102    
3103        /**
3104         * Tests this plot for equality with an arbitrary object.  Note that the
3105         * plot's dataset is NOT included in the test for equality.
3106         *
3107         * @param obj  the object to test against (<code>null</code> permitted).
3108         *
3109         * @return <code>true</code> or <code>false</code>.
3110         */
3111        public boolean equals(Object obj) {
3112            if (obj == this) {
3113                return true;
3114            }
3115            if (!(obj instanceof PiePlot)) {
3116                return false;
3117            }
3118            if (!super.equals(obj)) {
3119                return false;
3120            }
3121            PiePlot that = (PiePlot) obj;
3122            if (this.pieIndex != that.pieIndex) {
3123                return false;
3124            }
3125            if (this.interiorGap != that.interiorGap) {
3126                return false;
3127            }
3128            if (this.circular != that.circular) {
3129                return false;
3130            }
3131            if (this.startAngle != that.startAngle) {
3132                return false;
3133            }
3134            if (this.direction != that.direction) {
3135                return false;
3136            }
3137            if (this.ignoreZeroValues != that.ignoreZeroValues) {
3138                return false;
3139            }
3140            if (this.ignoreNullValues != that.ignoreNullValues) {
3141                return false;
3142            }
3143            if (!PaintUtilities.equal(this.sectionPaint, that.sectionPaint)) {
3144                return false;
3145            }
3146            if (!ObjectUtilities.equal(this.sectionPaintMap,
3147                    that.sectionPaintMap)) {
3148                return false;
3149            }
3150            if (!PaintUtilities.equal(this.baseSectionPaint,
3151                    that.baseSectionPaint)) {
3152                return false;
3153            }
3154            if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) {
3155                return false;
3156            }
3157            if (!PaintUtilities.equal(this.sectionOutlinePaint,
3158                    that.sectionOutlinePaint)) {
3159                return false;
3160            }
3161            if (!ObjectUtilities.equal(this.sectionOutlinePaintMap,
3162                    that.sectionOutlinePaintMap)) {
3163                return false;
3164            }
3165            if (!PaintUtilities.equal(
3166                this.baseSectionOutlinePaint, that.baseSectionOutlinePaint
3167            )) {
3168                return false;
3169            }
3170            if (!ObjectUtilities.equal(this.sectionOutlineStroke,
3171                    that.sectionOutlineStroke)) {
3172                return false;
3173            }
3174            if (!ObjectUtilities.equal(this.sectionOutlineStrokeMap,
3175                    that.sectionOutlineStrokeMap)) {
3176                return false;
3177            }
3178            if (!ObjectUtilities.equal(
3179                this.baseSectionOutlineStroke, that.baseSectionOutlineStroke
3180            )) {
3181                return false;
3182            }
3183            if (!PaintUtilities.equal(this.shadowPaint, that.shadowPaint)) {
3184                return false;
3185            }
3186            if (!(this.shadowXOffset == that.shadowXOffset)) {
3187                return false;
3188            }
3189            if (!(this.shadowYOffset == that.shadowYOffset)) {
3190                return false;
3191            }
3192            if (!ObjectUtilities.equal(this.explodePercentages,
3193                    that.explodePercentages)) {
3194                return false;
3195            }
3196            if (!ObjectUtilities.equal(this.labelGenerator,
3197                    that.labelGenerator)) {
3198                return false;
3199            }
3200            if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
3201                return false;
3202            }
3203            if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) {
3204                return false;
3205            }
3206            if (!PaintUtilities.equal(this.labelBackgroundPaint,
3207                    that.labelBackgroundPaint)) {
3208                return false;
3209            }
3210            if (!PaintUtilities.equal(this.labelOutlinePaint,
3211                    that.labelOutlinePaint)) {
3212                return false;
3213            }
3214            if (!ObjectUtilities.equal(this.labelOutlineStroke,
3215                    that.labelOutlineStroke)) {
3216                return false;
3217            }
3218            if (!PaintUtilities.equal(this.labelShadowPaint,
3219                    that.labelShadowPaint)) {
3220                return false;
3221            }
3222            if (this.simpleLabels != that.simpleLabels) {
3223                return false;
3224            }
3225            if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) {
3226                return false;
3227            }
3228            if (!this.labelPadding.equals(that.labelPadding)) {
3229                return false;
3230            }
3231            if (!(this.maximumLabelWidth == that.maximumLabelWidth)) {
3232                return false;
3233            }
3234            if (!(this.labelGap == that.labelGap)) {
3235                return false;
3236            }
3237            if (!(this.labelLinkMargin == that.labelLinkMargin)) {
3238                return false;
3239            }
3240            if (this.labelLinksVisible != that.labelLinksVisible) {
3241                return false;
3242            }
3243            if (!this.labelLinkStyle.equals(that.labelLinkStyle)) {
3244                return false;
3245            }
3246            if (!PaintUtilities.equal(this.labelLinkPaint, that.labelLinkPaint)) {
3247                return false;
3248            }
3249            if (!ObjectUtilities.equal(this.labelLinkStroke,
3250                    that.labelLinkStroke)) {
3251                return false;
3252            }
3253            if (!ObjectUtilities.equal(this.toolTipGenerator,
3254                    that.toolTipGenerator)) {
3255                return false;
3256            }
3257            if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
3258                return false;
3259            }
3260            if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
3261                return false;
3262            }
3263            if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) {
3264                return false;
3265            }
3266            if (!ObjectUtilities.equal(this.legendLabelGenerator,
3267                    that.legendLabelGenerator)) {
3268                return false;
3269            }
3270            if (!ObjectUtilities.equal(this.legendLabelToolTipGenerator,
3271                    that.legendLabelToolTipGenerator)) {
3272                return false;
3273            }
3274            if (!ObjectUtilities.equal(this.legendLabelURLGenerator,
3275                    that.legendLabelURLGenerator)) {
3276                return false;
3277            }
3278            if (this.autoPopulateSectionPaint != that.autoPopulateSectionPaint) {
3279                return false;
3280            }
3281            if (this.autoPopulateSectionOutlinePaint
3282                    != that.autoPopulateSectionOutlinePaint) {
3283                return false;
3284            }
3285            if (this.autoPopulateSectionOutlineStroke
3286                    != that.autoPopulateSectionOutlineStroke) {
3287                return false;
3288            }
3289            // can't find any difference...
3290            return true;
3291        }
3292    
3293        /**
3294         * Returns a clone of the plot.
3295         *
3296         * @return A clone.
3297         *
3298         * @throws CloneNotSupportedException if some component of the plot does
3299         *         not support cloning.
3300         */
3301        public Object clone() throws CloneNotSupportedException {
3302            PiePlot clone = (PiePlot) super.clone();
3303            if (clone.dataset != null) {
3304                clone.dataset.addChangeListener(clone);
3305            }
3306            if (this.urlGenerator instanceof PublicCloneable) {
3307                clone.urlGenerator = (PieURLGenerator) ObjectUtilities.clone(
3308                        this.urlGenerator);
3309            }
3310            clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape);
3311            if (this.legendLabelGenerator != null) {
3312                clone.legendLabelGenerator = (PieSectionLabelGenerator)
3313                        ObjectUtilities.clone(this.legendLabelGenerator);
3314            }
3315            if (this.legendLabelToolTipGenerator != null) {
3316                clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator)
3317                        ObjectUtilities.clone(this.legendLabelToolTipGenerator);
3318            }
3319            if (this.legendLabelURLGenerator instanceof PublicCloneable) {
3320                clone.legendLabelURLGenerator = (PieURLGenerator)
3321                        ObjectUtilities.clone(this.legendLabelURLGenerator);
3322            }
3323            return clone;
3324        }
3325    
3326        /**
3327         * Provides serialization support.
3328         *
3329         * @param stream  the output stream.
3330         *
3331         * @throws IOException  if there is an I/O error.
3332         */
3333        private void writeObject(ObjectOutputStream stream) throws IOException {
3334            stream.defaultWriteObject();
3335            SerialUtilities.writePaint(this.sectionPaint, stream);
3336            SerialUtilities.writePaint(this.baseSectionPaint, stream);
3337            SerialUtilities.writePaint(this.sectionOutlinePaint, stream);
3338            SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream);
3339            SerialUtilities.writeStroke(this.sectionOutlineStroke, stream);
3340            SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream);
3341            SerialUtilities.writePaint(this.shadowPaint, stream);
3342            SerialUtilities.writePaint(this.labelPaint, stream);
3343            SerialUtilities.writePaint(this.labelBackgroundPaint, stream);
3344            SerialUtilities.writePaint(this.labelOutlinePaint, stream);
3345            SerialUtilities.writeStroke(this.labelOutlineStroke, stream);
3346            SerialUtilities.writePaint(this.labelShadowPaint, stream);
3347            SerialUtilities.writePaint(this.labelLinkPaint, stream);
3348            SerialUtilities.writeStroke(this.labelLinkStroke, stream);
3349            SerialUtilities.writeShape(this.legendItemShape, stream);
3350        }
3351    
3352        /**
3353         * Provides serialization support.
3354         *
3355         * @param stream  the input stream.
3356         *
3357         * @throws IOException  if there is an I/O error.
3358         * @throws ClassNotFoundException  if there is a classpath problem.
3359         */
3360        private void readObject(ObjectInputStream stream)
3361            throws IOException, ClassNotFoundException {
3362            stream.defaultReadObject();
3363            this.sectionPaint = SerialUtilities.readPaint(stream);
3364            this.baseSectionPaint = SerialUtilities.readPaint(stream);
3365            this.sectionOutlinePaint = SerialUtilities.readPaint(stream);
3366            this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream);
3367            this.sectionOutlineStroke = SerialUtilities.readStroke(stream);
3368            this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream);
3369            this.shadowPaint = SerialUtilities.readPaint(stream);
3370            this.labelPaint = SerialUtilities.readPaint(stream);
3371            this.labelBackgroundPaint = SerialUtilities.readPaint(stream);
3372            this.labelOutlinePaint = SerialUtilities.readPaint(stream);
3373            this.labelOutlineStroke = SerialUtilities.readStroke(stream);
3374            this.labelShadowPaint = SerialUtilities.readPaint(stream);
3375            this.labelLinkPaint = SerialUtilities.readPaint(stream);
3376            this.labelLinkStroke = SerialUtilities.readStroke(stream);
3377            this.legendItemShape = SerialUtilities.readShape(stream);
3378        }
3379    
3380        // DEPRECATED FIELDS AND METHODS...
3381    
3382        /**
3383         * The paint for ALL sections (overrides list).
3384         *
3385         * @deprecated This field is redundant, it is sufficient to use
3386         *     sectionPaintMap and baseSectionPaint.  Deprecated as of version
3387         *     1.0.6.
3388         */
3389        private transient Paint sectionPaint;
3390    
3391        /**
3392         * The outline paint for ALL sections (overrides list).
3393         *
3394         * @deprecated This field is redundant, it is sufficient to use
3395         *     sectionOutlinePaintMap and baseSectionOutlinePaint.  Deprecated as
3396         *     of version 1.0.6.
3397         */
3398        private transient Paint sectionOutlinePaint;
3399    
3400        /**
3401         * The outline stroke for ALL sections (overrides list).
3402         *
3403         * @deprecated This field is redundant, it is sufficient to use
3404         *     sectionOutlineStrokeMap and baseSectionOutlineStroke.  Deprecated as
3405         *     of version 1.0.6.
3406         */
3407        private transient Stroke sectionOutlineStroke;
3408    
3409        /**
3410         * Returns the paint for the specified section.
3411         *
3412         * @param section  the section index (zero-based).
3413         *
3414         * @return The paint (never <code>null</code>).
3415         *
3416         * @deprecated Use {@link #getSectionPaint(Comparable)} instead.
3417         */
3418        public Paint getSectionPaint(int section) {
3419            Comparable key = getSectionKey(section);
3420            return getSectionPaint(key);
3421        }
3422    
3423        /**
3424         * Sets the paint used to fill a section of the pie and sends a
3425         * {@link PlotChangeEvent} to all registered listeners.
3426         *
3427         * @param section  the section index (zero-based).
3428         * @param paint  the paint (<code>null</code> permitted).
3429         *
3430         * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} instead.
3431         */
3432        public void setSectionPaint(int section, Paint paint) {
3433            Comparable key = getSectionKey(section);
3434            setSectionPaint(key, paint);
3435        }
3436    
3437        /**
3438         * Returns the outline paint for ALL sections in the plot.
3439         *
3440         * @return The paint (possibly <code>null</code>).
3441         *
3442         * @see #setSectionOutlinePaint(Paint)
3443         *
3444         * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} and
3445         *     {@link #getBaseSectionOutlinePaint()}.  Deprecated as of version
3446         *     1.0.6.
3447         */
3448        public Paint getSectionOutlinePaint() {
3449            return this.sectionOutlinePaint;
3450        }
3451    
3452        /**
3453         * Sets the outline paint for ALL sections in the plot.  If this is set to
3454         * </code>null</code>, then a list of paints is used instead (to allow
3455         * different colors to be used for each section).
3456         *
3457         * @param paint  the paint (<code>null</code> permitted).
3458         *
3459         * @see #getSectionOutlinePaint()
3460         *
3461         * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)} and
3462         *     {@link #setBaseSectionOutlinePaint(Paint)}.  Deprecated as of
3463         *     version 1.0.6.
3464         */
3465        public void setSectionOutlinePaint(Paint paint) {
3466            this.sectionOutlinePaint = paint;
3467            fireChangeEvent();
3468        }
3469    
3470        /**
3471         * Returns the paint for the specified section.
3472         *
3473         * @param section  the section index (zero-based).
3474         *
3475         * @return The paint (possibly <code>null</code>).
3476         *
3477         * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} instead.
3478         */
3479        public Paint getSectionOutlinePaint(int section) {
3480            Comparable key = getSectionKey(section);
3481            return getSectionOutlinePaint(key);
3482        }
3483    
3484        /**
3485         * Sets the paint used to fill a section of the pie and sends a
3486         * {@link PlotChangeEvent} to all registered listeners.
3487         *
3488         * @param section  the section index (zero-based).
3489         * @param paint  the paint (<code>null</code> permitted).
3490         *
3491         * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)}
3492         *     instead.
3493         */
3494        public void setSectionOutlinePaint(int section, Paint paint) {
3495            Comparable key = getSectionKey(section);
3496            setSectionOutlinePaint(key, paint);
3497        }
3498    
3499        /**
3500         * Returns the outline stroke for ALL sections in the plot.
3501         *
3502         * @return The stroke (possibly <code>null</code>).
3503         *
3504         * @see #setSectionOutlineStroke(Stroke)
3505         *
3506         * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} and
3507         *     {@link #getBaseSectionOutlineStroke()}.  Deprecated as of version
3508         *     1.0.6.
3509         */
3510        public Stroke getSectionOutlineStroke() {
3511            return this.sectionOutlineStroke;
3512        }
3513    
3514        /**
3515         * Sets the outline stroke for ALL sections in the plot.  If this is set to
3516         * </code>null</code>, then a list of paints is used instead (to allow
3517         * different colors to be used for each section).
3518         *
3519         * @param stroke  the stroke (<code>null</code> permitted).
3520         *
3521         * @see #getSectionOutlineStroke()
3522         *
3523         * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} and
3524         *     {@link #setBaseSectionOutlineStroke(Stroke)}.  Deprecated as of
3525         *     version 1.0.6.
3526         */
3527        public void setSectionOutlineStroke(Stroke stroke) {
3528            this.sectionOutlineStroke = stroke;
3529            fireChangeEvent();
3530        }
3531    
3532        /**
3533         * Returns the stroke for the specified section.
3534         *
3535         * @param section  the section index (zero-based).
3536         *
3537         * @return The stroke (possibly <code>null</code>).
3538         *
3539         * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} instead.
3540         */
3541        public Stroke getSectionOutlineStroke(int section) {
3542            Comparable key = getSectionKey(section);
3543            return getSectionOutlineStroke(key);
3544        }
3545    
3546        /**
3547         * Sets the stroke used to fill a section of the pie and sends a
3548         * {@link PlotChangeEvent} to all registered listeners.
3549         *
3550         * @param section  the section index (zero-based).
3551         * @param stroke  the stroke (<code>null</code> permitted).
3552         *
3553         * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)}
3554         *     instead.
3555         */
3556        public void setSectionOutlineStroke(int section, Stroke stroke) {
3557            Comparable key = getSectionKey(section);
3558            setSectionOutlineStroke(key, stroke);
3559        }
3560    
3561        /**
3562         * Returns the amount that a section should be 'exploded'.
3563         *
3564         * @param section  the section number.
3565         *
3566         * @return The amount that a section should be 'exploded'.
3567         *
3568         * @deprecated Use {@link #getExplodePercent(Comparable)} instead.
3569         */
3570        public double getExplodePercent(int section) {
3571            Comparable key = getSectionKey(section);
3572            return getExplodePercent(key);
3573        }
3574    
3575        /**
3576         * Sets the amount that a pie section should be exploded and sends a
3577         * {@link PlotChangeEvent} to all registered listeners.
3578         *
3579         * @param section  the section index.
3580         * @param percent  the explode percentage (0.30 = 30 percent).
3581         *
3582         * @deprecated Use {@link #setExplodePercent(Comparable, double)} instead.
3583         */
3584        public void setExplodePercent(int section, double percent) {
3585            Comparable key = getSectionKey(section);
3586            setExplodePercent(key, percent);
3587        }
3588    
3589    }