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