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