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