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