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