001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * ------------
028     * PiePlot.java
029     * ------------
030     * (C) Copyright 2000-2007, by Andrzej Porebski and Contributors.
031     *
032     * Original Author:  Andrzej Porebski;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *                   Martin Cordova (percentages in labels);
035     *                   Richard Atkinson (URL support for image maps);
036     *                   Christian W. Zuckschwerdt;
037     *                   Arnaud Lelievre;
038     *                   Andreas Schroeder (very minor);
039     *
040     * $Id: PiePlot.java,v 1.17.2.25 2007/06/14 15:04:21 mungady Exp $
041     *
042     * Changes (from 21-Jun-2001)
043     * --------------------------
044     * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
045     * 18-Sep-2001 : Updated header (DG);
046     * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
047     * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to 
048     *               Plot.java (DG);
049     * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
050     * 13-Nov-2001 : Modified plot subclasses so that null axes are possible for 
051     *               pie plot (DG);
052     * 17-Nov-2001 : Added PieDataset interface and amended this class accordingly,
053     *               and completed removal of BlankAxis class as it is no longer 
054     *               required (DG);
055     * 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG);
056     * 21-Nov-2001 : Added options for exploding pie sections and filled out range 
057     *               of properties (DG);
058     *               Added option for percentages in chart labels, based on code
059     *               by Martin Cordova (DG);
060     * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
061     * 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG);
062     * 13-Dec-2001 : Added tooltips (DG);
063     * 16-Jan-2002 : Renamed tooltips class (DG);
064     * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
065     * 05-Feb-2002 : Added alpha-transparency to plot class, and updated 
066     *               constructors accordingly (DG);
067     * 06-Feb-2002 : Added optional background image and alpha-transparency to Plot
068     *               and subclasses.  Clipped drawing within plot area (DG);
069     * 26-Mar-2002 : Added an empty zoom method (DG);
070     * 18-Apr-2002 : PieDataset is no longer sorted (oldman);
071     * 23-Apr-2002 : Moved dataset from JFreeChart to Plot.  Added 
072     *               getLegendItemLabels() method (DG);
073     * 19-Jun-2002 : Added attributes to control starting angle and direction 
074     *               (default is now clockwise) (DG);
075     * 25-Jun-2002 : Removed redundant imports (DG);
076     * 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG);
077     * 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG);
078     * 30-Jul-2002 : Moved summation code to DatasetUtilities (DG);
079     * 05-Aug-2002 : Added URL support for image maps - new member variable for
080     *               urlGenerator, modified constructor and minor change to the 
081     *               draw method (RA);
082     * 18-Sep-2002 : Modified the percent label creation and added setters for the
083     *               formatters (AS);
084     * 24-Sep-2002 : Added getLegendItems() method (DG);
085     * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
086     * 09-Oct-2002 : Added check for null entity collection (DG);
087     * 30-Oct-2002 : Changed PieDataset interface (DG);
088     * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
089     * 02-Jan-2003 : Fixed "no data" message (DG);
090     * 23-Jan-2003 : Modified to extract data from rows OR columns in 
091     *               CategoryDataset (DG);
092     * 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply 
093     *               (bug id 685536) (DG);
094     * 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip 
095     *               and URL generators (DG);
096     * 21-Mar-2003 : Added a minimum angle for drawing arcs 
097     *               (see bug id 620031) (DG);
098     * 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG);
099     * 02-Jun-2003 : Fixed bug 721733 (DG);
100     * 30-Jul-2003 : Modified entity constructor (CZ);
101     * 19-Aug-2003 : Implemented Cloneable (DG);
102     * 29-Aug-2003 : Fixed bug 796936 (null pointer on setOutlinePaint()) (DG);
103     * 08-Sep-2003 : Added internationalization via use of properties 
104     *               resourceBundle (RFE 690236) (AL);
105     * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
106     * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
107     * 05-Nov-2003 : Fixed missing legend bug (DG);
108     * 10-Nov-2003 : Re-added the DatasetChangeListener to constructors (CZ);
109     * 29-Jan-2004 : Fixed clipping bug in draw() method (DG);
110     * 11-Mar-2004 : Major overhaul to improve labelling (DG);
111     * 31-Mar-2004 : Made an adjustment for the plot area when the label generator 
112     *               is null.  Fixed null pointer exception when the label 
113     *               generator returns null for a label (DG);
114     * 06-Apr-2004 : Added getter, setter, serialization and draw support for 
115     *               labelBackgroundPaint (AS);
116     * 08-Apr-2004 : Added flag to control whether null values are ignored or 
117     *               not (DG);
118     * 15-Apr-2004 : Fixed some minor warnings from Eclipse (DG);
119     * 26-Apr-2004 : Added attributes for label outline and shadow (DG);
120     * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
121     * 04-Nov-2004 : Fixed null pointer exception with new LegendTitle class (DG);
122     * 09-Nov-2004 : Added user definable legend item shape (DG);
123     * 25-Nov-2004 : Added new legend label generator (DG);
124     * 20-Apr-2005 : Added a tool tip generator for legend labels (DG);
125     * 26-Apr-2005 : Removed LOGGER (DG);
126     * 05-May-2005 : Updated draw() method parameters (DG);
127     * 10-May-2005 : Added flag to control visibility of label linking lines, plus
128     *               another flag to control the handling of zero values (DG);
129     * 08-Jun-2005 : Fixed bug in getLegendItems() method (not respecting flags
130     *               for ignoring null and zero values), and fixed equals() method 
131     *               to handle GradientPaint (DG);
132     * 15-Jul-2005 : Added sectionOutlinesVisible attribute (DG);
133     * ------------- JFREECHART 1.0.x ---------------------------------------------
134     * 09-Jan-2006 : Fixed bug 1400442, inconsistent treatment of null and zero
135     *               values in dataset (DG);
136     * 28-Feb-2006 : Fixed bug 1440415, bad distribution of pie section 
137     *               labels (DG);
138     * 27-Sep-2006 : Initialised baseSectionPaint correctly, added lookup methods
139     *               for section paint, outline paint and outline stroke (DG);
140     * 27-Sep-2006 : Refactored paint and stroke methods to use keys rather than
141     *               section indices (DG);
142     * 03-Oct-2006 : Replaced call to JRE 1.5 method (DG);
143     * 23-Nov-2006 : Added support for URLs for the legend items (DG);
144     * 24-Nov-2006 : Cloning fixes (DG);
145     * 17-Apr-2007 : Check for null label in legend items (DG);
146     * 19-Apr-2007 : Deprecated override settings (DG);
147     * 18-May-2007 : Set dataset for LegendItem (DG);
148     * 14-Jun-2007 : Added label distributor attribute (DG);
149     *          
150     */
151    
152    package org.jfree.chart.plot;
153    
154    import java.awt.AlphaComposite;
155    import java.awt.BasicStroke;
156    import java.awt.Color;
157    import java.awt.Composite;
158    import java.awt.Font;
159    import java.awt.Graphics2D;
160    import java.awt.Paint;
161    import java.awt.Shape;
162    import java.awt.Stroke;
163    import java.awt.geom.Arc2D;
164    import java.awt.geom.Line2D;
165    import java.awt.geom.Point2D;
166    import java.awt.geom.Rectangle2D;
167    import java.io.IOException;
168    import java.io.ObjectInputStream;
169    import java.io.ObjectOutputStream;
170    import java.io.Serializable;
171    import java.util.Iterator;
172    import java.util.List;
173    import java.util.Map;
174    import java.util.ResourceBundle;
175    import java.util.TreeMap;
176    
177    import org.jfree.chart.LegendItem;
178    import org.jfree.chart.LegendItemCollection;
179    import org.jfree.chart.PaintMap;
180    import org.jfree.chart.StrokeMap;
181    import org.jfree.chart.entity.EntityCollection;
182    import org.jfree.chart.entity.PieSectionEntity;
183    import org.jfree.chart.event.PlotChangeEvent;
184    import org.jfree.chart.labels.PieSectionLabelGenerator;
185    import org.jfree.chart.labels.PieToolTipGenerator;
186    import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
187    import org.jfree.chart.urls.PieURLGenerator;
188    import org.jfree.data.DefaultKeyedValues;
189    import org.jfree.data.KeyedValues;
190    import org.jfree.data.general.DatasetChangeEvent;
191    import org.jfree.data.general.DatasetUtilities;
192    import org.jfree.data.general.PieDataset;
193    import org.jfree.io.SerialUtilities;
194    import org.jfree.text.G2TextMeasurer;
195    import org.jfree.text.TextBlock;
196    import org.jfree.text.TextBox;
197    import org.jfree.text.TextUtilities;
198    import org.jfree.ui.RectangleAnchor;
199    import org.jfree.ui.RectangleInsets;
200    import org.jfree.util.ObjectUtilities;
201    import org.jfree.util.PaintUtilities;
202    import org.jfree.util.PublicCloneable;
203    import org.jfree.util.Rotation;
204    import org.jfree.util.ShapeUtilities;
205    
206    /**
207     * A plot that displays data in the form of a pie chart, using data from any 
208     * class that implements the {@link PieDataset} interface.
209     * <P>
210     * Special notes:
211     * <ol>
212     * <li>the default starting point is 12 o'clock and the pie sections proceed
213     * in a clockwise direction, but these settings can be changed;</li>
214     * <li>negative values in the dataset are ignored;</li>
215     * <li>there are utility methods for creating a {@link PieDataset} from a
216     * {@link org.jfree.data.category.CategoryDataset};</li>
217     * </ol>
218     *
219     * @see Plot
220     * @see PieDataset
221     */
222    public class PiePlot extends Plot implements Cloneable, Serializable {
223        
224        /** For serialization. */
225        private static final long serialVersionUID = -795612466005590431L;
226        
227        /** The default interior gap. */
228        public static final double DEFAULT_INTERIOR_GAP = 0.25;
229    
230        /** The maximum interior gap (currently 40%). */
231        public static final double MAX_INTERIOR_GAP = 0.40;
232    
233        /** The default starting angle for the pie chart. */
234        public static final double DEFAULT_START_ANGLE = 90.0;
235    
236        /** The default section label font. */
237        public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", 
238                Font.PLAIN, 10);
239    
240        /** The default section label paint. */
241        public static final Paint DEFAULT_LABEL_PAINT = Color.black;
242        
243        /** The default section label background paint. */
244        public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255, 
245                255, 192);
246    
247        /** The default section label outline paint. */
248        public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
249        
250        /** The default section label outline stroke. */
251        public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(
252                0.5f);
253        
254        /** The default section label shadow paint. */
255        public static final Paint DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray;
256        
257        /** The default minimum arc angle to draw. */
258        public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
259    
260        /** The dataset for the pie chart. */
261        private PieDataset dataset;
262    
263        /** The pie index (used by the {@link MultiplePiePlot} class). */
264        private int pieIndex;
265    
266        /** 
267         * The amount of space left around the outside of the pie plot, expressed 
268         * as a percentage. 
269         */
270        private double interiorGap;
271    
272        /** Flag determining whether to draw an ellipse or a perfect circle. */
273        private boolean circular;
274    
275        /** The starting angle. */
276        private double startAngle;
277    
278        /** The direction for the pie segments. */
279        private Rotation direction;
280    
281        /** 
282         * The paint for ALL sections (overrides list).
283         * 
284         * @deprecated This field is redundant, it is sufficient to use 
285         *     sectionPaintMap and baseSectionPaint.  Deprecated as of version 
286         *     1.0.6.
287         */
288        private transient Paint sectionPaint;
289    
290        /** The section paint map. */
291        private PaintMap sectionPaintMap;
292    
293        /** The base section paint (fallback). */
294        private transient Paint baseSectionPaint;
295    
296        /** 
297         * A flag that controls whether or not an outline is drawn for each
298         * section in the plot.
299         */
300        private boolean sectionOutlinesVisible;
301    
302        /** 
303         * The outline paint for ALL sections (overrides list). 
304         * 
305         * @deprecated This field is redundant, it is sufficient to use 
306         *     sectionOutlinePaintMap and baseSectionOutlinePaint.  Deprecated as 
307         *     of version 1.0.6.
308         */
309        private transient Paint sectionOutlinePaint;
310    
311        /** The section outline paint map. */
312        private PaintMap sectionOutlinePaintMap;
313    
314        /** The base section outline paint (fallback). */
315        private transient Paint baseSectionOutlinePaint;
316    
317        /** 
318         * The outline stroke for ALL sections (overrides list). 
319         * 
320         * @deprecated This field is redundant, it is sufficient to use 
321         *     sectionOutlineStrokeMap and baseSectionOutlineStroke.  Deprecated as 
322         *     of version 1.0.6.
323         */
324        private transient Stroke sectionOutlineStroke;
325    
326        /** The section outline stroke map. */
327        private StrokeMap sectionOutlineStrokeMap;
328    
329        /** The base section outline stroke (fallback). */
330        private transient Stroke baseSectionOutlineStroke;
331    
332        /** The shadow paint. */
333        private transient Paint shadowPaint = Color.gray;
334    
335        /** The x-offset for the shadow effect. */
336        private double shadowXOffset = 4.0f;
337        
338        /** The y-offset for the shadow effect. */
339        private double shadowYOffset = 4.0f;
340        
341        /** The percentage amount to explode each pie section. */
342        private Map explodePercentages;
343        
344        /** The section label generator. */
345        private PieSectionLabelGenerator labelGenerator;
346    
347        /** The font used to display the section labels. */
348        private Font labelFont;
349    
350        /** The color used to draw the section labels. */
351        private transient Paint labelPaint;
352        
353        /** The color used to draw the background of the section labels. */
354        private transient Paint labelBackgroundPaint;
355    
356        /** 
357         * The paint used to draw the outline of the section labels 
358         * (<code>null</code> permitted). 
359         */
360        private transient Paint labelOutlinePaint;
361        
362        /** 
363         * The stroke used to draw the outline of the section labels 
364         * (<code>null</code> permitted). 
365         */
366        private transient Stroke labelOutlineStroke;
367        
368        /** 
369         * The paint used to draw the shadow for the section labels 
370         * (<code>null</code> permitted). 
371         */
372        private transient Paint labelShadowPaint;
373        
374        /** The maximum label width as a percentage of the plot width. */
375        private double maximumLabelWidth = 0.20;
376        
377        /** 
378         * The gap between the labels and the plot as a percentage of the plot 
379         * width. 
380         */
381        private double labelGap = 0.05;
382    
383        /** A flag that controls whether or not the label links are drawn. */
384        private boolean labelLinksVisible;
385        
386        /** The link margin. */
387        private double labelLinkMargin = 0.05;
388        
389        /** The paint used for the label linking lines. */
390        private transient Paint labelLinkPaint = Color.black;
391        
392        /** The stroke used for the label linking lines. */
393        private transient Stroke labelLinkStroke = new BasicStroke(0.5f);
394        
395        /** 
396         * The pie section label distributor.
397         * 
398         * @since 1.0.6
399         */
400        private AbstractPieLabelDistributor labelDistributor;
401        
402        /** The tooltip generator. */
403        private PieToolTipGenerator toolTipGenerator;
404    
405        /** The URL generator. */
406        private PieURLGenerator urlGenerator;
407        
408        /** The legend label generator. */
409        private PieSectionLabelGenerator legendLabelGenerator;
410        
411        /** A tool tip generator for the legend. */
412        private PieSectionLabelGenerator legendLabelToolTipGenerator;
413        
414        /** 
415         * A URL generator for the legend items (optional).  
416         *
417         * @since 1.0.4. 
418         */
419        private PieURLGenerator legendLabelURLGenerator;
420        
421        /** 
422         * A flag that controls whether <code>null</code> values are ignored.  
423         */
424        private boolean ignoreNullValues;
425        
426        /**
427         * A flag that controls whether zero values are ignored.
428         */
429        private boolean ignoreZeroValues;
430    
431        /** The legend item shape. */
432        private transient Shape legendItemShape;
433        
434        /**
435         * The smallest arc angle that will get drawn (this is to avoid a bug in 
436         * various Java implementations that causes the JVM to crash).  See this 
437         * link for details:
438         *
439         * http://www.jfree.org/phpBB2/viewtopic.php?t=2707
440         *
441         * ...and this bug report in the Java Bug Parade:
442         *
443         * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html
444         */
445        private double minimumArcAngleToDraw;
446    
447        /** The resourceBundle for the localization. */
448        protected static ResourceBundle localizationResources =
449                ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
450    
451        /**
452         * Creates a new plot.  The dataset is initially set to <code>null</code>.
453         */
454        public PiePlot() {
455            this(null);
456        }
457    
458        /**
459         * Creates a plot that will draw a pie chart for the specified dataset.
460         *
461         * @param dataset  the dataset (<code>null</code> permitted).
462         */
463        public PiePlot(PieDataset dataset) {
464            super();
465            this.dataset = dataset;
466            if (dataset != null) {
467                dataset.addChangeListener(this);
468            }
469            this.pieIndex = 0;
470            
471            this.interiorGap = DEFAULT_INTERIOR_GAP;
472            this.circular = true;
473            this.startAngle = DEFAULT_START_ANGLE;
474            this.direction = Rotation.CLOCKWISE;
475            this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
476            
477            this.sectionPaint = null;
478            this.sectionPaintMap = new PaintMap();
479            this.baseSectionPaint = Color.gray;
480    
481            this.sectionOutlinesVisible = true;
482            this.sectionOutlinePaint = null;
483            this.sectionOutlinePaintMap = new PaintMap();
484            this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
485    
486            this.sectionOutlineStroke = null;
487            this.sectionOutlineStrokeMap = new StrokeMap();
488            this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
489            
490            this.explodePercentages = new TreeMap();
491    
492            this.labelGenerator = new StandardPieSectionLabelGenerator();
493            this.labelFont = DEFAULT_LABEL_FONT;
494            this.labelPaint = DEFAULT_LABEL_PAINT;
495            this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
496            this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
497            this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
498            this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
499            this.labelLinksVisible = true;
500            this.labelDistributor = new PieLabelDistributor(0);
501            
502            this.toolTipGenerator = null;
503            this.urlGenerator = null;
504            this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
505            this.legendLabelToolTipGenerator = null;
506            this.legendLabelURLGenerator = null;
507            this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
508            
509            this.ignoreNullValues = false;
510            this.ignoreZeroValues = false;
511        }
512    
513        /**
514         * Returns the dataset.
515         *
516         * @return The dataset (possibly <code>null</code>).
517         * 
518         * @see #setDataset(PieDataset)
519         */
520        public PieDataset getDataset() {
521            return this.dataset;
522        }
523    
524        /**
525         * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'.
526         *
527         * @param dataset  the dataset (<code>null</code> permitted).
528         * 
529         * @see #getDataset()
530         */
531        public void setDataset(PieDataset dataset) {
532            // if there is an existing dataset, remove the plot from the list of 
533            // change listeners...
534            PieDataset existing = this.dataset;
535            if (existing != null) {
536                existing.removeChangeListener(this);
537            }
538    
539            // set the new dataset, and register the chart as a change listener...
540            this.dataset = dataset;
541            if (dataset != null) {
542                setDatasetGroup(dataset.getGroup());
543                dataset.addChangeListener(this);
544            }
545    
546            // send a dataset change event to self...
547            DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
548            datasetChanged(event);
549        }
550        
551        /**
552         * Returns the pie index (this is used by the {@link MultiplePiePlot} class
553         * to track subplots).
554         * 
555         * @return The pie index.
556         * 
557         * @see #setPieIndex(int)
558         */
559        public int getPieIndex() {
560            return this.pieIndex;
561        }
562        
563        /**
564         * Sets the pie index (this is used by the {@link MultiplePiePlot} class to 
565         * track subplots).
566         * 
567         * @param index  the index.
568         * 
569         * @see #getPieIndex()
570         */
571        public void setPieIndex(int index) {
572            this.pieIndex = index;
573        }
574        
575        /**
576         * Returns the start angle for the first pie section.  This is measured in 
577         * degrees starting from 3 o'clock and measuring anti-clockwise.
578         *
579         * @return The start angle.
580         * 
581         * @see #setStartAngle(double)
582         */
583        public double getStartAngle() {
584            return this.startAngle;
585        }
586    
587        /**
588         * Sets the starting angle and sends a {@link PlotChangeEvent} to all 
589         * registered listeners.  The initial default value is 90 degrees, which 
590         * corresponds to 12 o'clock.  A value of zero corresponds to 3 o'clock...
591         * this is the encoding used by Java's Arc2D class.
592         *
593         * @param angle  the angle (in degrees).
594         * 
595         * @see #getStartAngle()
596         */
597        public void setStartAngle(double angle) {
598            this.startAngle = angle;
599            notifyListeners(new PlotChangeEvent(this));
600        }
601    
602        /**
603         * Returns the direction in which the pie sections are drawn (clockwise or 
604         * anti-clockwise).
605         *
606         * @return The direction (never <code>null</code>).
607         * 
608         * @see #setDirection(Rotation)
609         */
610        public Rotation getDirection() {
611            return this.direction;
612        }
613    
614        /**
615         * Sets the direction in which the pie sections are drawn and sends a 
616         * {@link PlotChangeEvent} to all registered listeners.
617         *
618         * @param direction  the direction (<code>null</code> not permitted).
619         * 
620         * @see #getDirection()
621         */
622        public void setDirection(Rotation direction) {
623            if (direction == null) {
624                throw new IllegalArgumentException("Null 'direction' argument.");
625            }
626            this.direction = direction;
627            notifyListeners(new PlotChangeEvent(this));
628    
629        }
630    
631        /**
632         * Returns the interior gap, measured as a percentage of the available 
633         * drawing space.
634         *
635         * @return The gap (as a percentage of the available drawing space).
636         * 
637         * @see #setInteriorGap(double)
638         */
639        public double getInteriorGap() {
640            return this.interiorGap;
641        }
642    
643        /**
644         * Sets the interior gap and sends a {@link PlotChangeEvent} to all 
645         * registered listeners.  This controls the space between the edges of the 
646         * pie plot and the plot area itself (the region where the section labels 
647         * appear).
648         *
649         * @param percent  the gap (as a percentage of the available drawing space).
650         * 
651         * @see #getInteriorGap()
652         */
653        public void setInteriorGap(double percent) {
654    
655            // check arguments...
656            if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
657                throw new IllegalArgumentException(
658                    "Invalid 'percent' (" + percent + ") argument.");
659            }
660    
661            // make the change...
662            if (this.interiorGap != percent) {
663                this.interiorGap = percent;
664                notifyListeners(new PlotChangeEvent(this));
665            }
666    
667        }
668    
669        /**
670         * Returns a flag indicating whether the pie chart is circular, or
671         * stretched into an elliptical shape.
672         *
673         * @return A flag indicating whether the pie chart is circular.
674         * 
675         * @see #setCircular(boolean)
676         */
677        public boolean isCircular() {
678            return this.circular;
679        }
680    
681        /**
682         * A flag indicating whether the pie chart is circular, or stretched into
683         * an elliptical shape.
684         *
685         * @param flag  the new value.
686         * 
687         * @see #isCircular()
688         */
689        public void setCircular(boolean flag) {
690            setCircular(flag, true);
691        }
692    
693        /**
694         * Sets the circular attribute and, if requested, sends a 
695         * {@link PlotChangeEvent} to all registered listeners.
696         *
697         * @param circular  the new value of the flag.
698         * @param notify  notify listeners?
699         * 
700         * @see #isCircular()
701         */
702        public void setCircular(boolean circular, boolean notify) {
703            this.circular = circular;
704            if (notify) {
705                notifyListeners(new PlotChangeEvent(this));   
706            }
707        }
708    
709        /**
710         * Returns the flag that controls whether <code>null</code> values in the 
711         * dataset are ignored.  
712         * 
713         * @return A boolean.
714         * 
715         * @see #setIgnoreNullValues(boolean)
716         */
717        public boolean getIgnoreNullValues() {
718            return this.ignoreNullValues;   
719        }
720        
721        /**
722         * Sets a flag that controls whether <code>null</code> values are ignored, 
723         * and sends a {@link PlotChangeEvent} to all registered listeners.  At 
724         * present, this only affects whether or not the key is presented in the 
725         * legend.
726         * 
727         * @param flag  the flag.
728         * 
729         * @see #getIgnoreNullValues()
730         * @see #setIgnoreZeroValues(boolean)
731         */
732        public void setIgnoreNullValues(boolean flag) {
733            this.ignoreNullValues = flag;
734            notifyListeners(new PlotChangeEvent(this));
735        }
736        
737        /**
738         * Returns the flag that controls whether zero values in the 
739         * dataset are ignored.  
740         * 
741         * @return A boolean.
742         * 
743         * @see #setIgnoreZeroValues(boolean)
744         */
745        public boolean getIgnoreZeroValues() {
746            return this.ignoreZeroValues;   
747        }
748        
749        /**
750         * Sets a flag that controls whether zero values are ignored, 
751         * and sends a {@link PlotChangeEvent} to all registered listeners.  This 
752         * only affects whether or not a label appears for the non-visible
753         * pie section.
754         * 
755         * @param flag  the flag.
756         * 
757         * @see #getIgnoreZeroValues()
758         * @see #setIgnoreNullValues(boolean)
759         */
760        public void setIgnoreZeroValues(boolean flag) {
761            this.ignoreZeroValues = flag;
762            notifyListeners(new PlotChangeEvent(this));
763        }
764        
765        //// SECTION PAINT ////////////////////////////////////////////////////////
766    
767        /**
768         * Returns the paint for the specified section.  This is equivalent to
769         * <code>lookupSectionPaint(section, false)</code>.
770         * 
771         * @param key  the section key.
772         * 
773         * @return The paint for the specified section.
774         * 
775         * @since 1.0.3
776         * 
777         * @see #lookupSectionPaint(Comparable, boolean)
778         */
779        protected Paint lookupSectionPaint(Comparable key) {
780            return lookupSectionPaint(key, false);        
781        }
782        
783        /**
784         * Returns the paint for the specified section.  The lookup involves these
785         * steps:
786         * <ul>
787         * <li>if {@link #getSectionPaint()} is non-<code>null</code>, return 
788         *         it;</li>
789         * <li>if {@link #getSectionPaint(int)} is non-<code>null</code> return 
790         *         it;</li>
791         * <li>if {@link #getSectionPaint(int)} is <code>null</code> but 
792         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
793         *         a new paint from the drawing supplier 
794         *         ({@link #getDrawingSupplier()});
795         * <li>if all else fails, return {@link #getBaseSectionPaint()}.
796         * </ul> 
797         * 
798         * @param key  the section key.
799         * @param autoPopulate  a flag that controls whether the drawing supplier 
800         *     is used to auto-populate the section paint settings.
801         *     
802         * @return The paint.
803         * 
804         * @since 1.0.3
805         */
806        protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) {
807            
808            // is there an override?
809            Paint result = getSectionPaint();
810            if (result != null) {
811                return result;
812            }
813            
814            // if not, check if there is a paint defined for the specified key
815            result = this.sectionPaintMap.getPaint(key);
816            if (result != null) {
817                return result;
818            }
819            
820            // nothing defined - do we autoPopulate?
821            if (autoPopulate) {
822                DrawingSupplier ds = getDrawingSupplier();
823                if (ds != null) {
824                    result = ds.getNextPaint();
825                    this.sectionPaintMap.put(key, result);
826                }
827                else {
828                    result = this.baseSectionPaint;
829                }
830            }
831            else {
832                result = this.baseSectionPaint;
833            }
834            return result;
835        }
836        
837        /**
838         * Returns the paint for ALL sections in the plot.
839         *
840         * @return The paint (possibly <code>null</code>).
841         * 
842         * @see #setSectionPaint(Paint)
843         * 
844         * @deprecated Use {@link #getSectionPaint(Comparable)} and 
845         *     {@link #getBaseSectionPaint()}.  Deprecated as of version 1.0.6.
846         */
847        public Paint getSectionPaint() {
848            return this.sectionPaint;
849        }
850    
851        /**
852         * Sets the paint for ALL sections in the plot.  If this is set to
853         * </code>null</code>, then a list of paints is used instead (to allow
854         * different colors to be used for each section).
855         *
856         * @param paint  the paint (<code>null</code> permitted).
857         * 
858         * @see #getSectionPaint()
859         * 
860         * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} and 
861         *     {@link #setBaseSectionPaint(Paint)}.  Deprecated as of version 1.0.6.
862         */
863        public void setSectionPaint(Paint paint) {
864            this.sectionPaint = paint;
865            notifyListeners(new PlotChangeEvent(this));
866        }
867    
868        /**
869         * Returns a key for the specified section.  If there is no such section 
870         * in the dataset, we generate a key.  This is to provide some backward
871         * compatibility for the (now deprecated) methods that get/set attributes 
872         * based on section indices.  The preferred way of doing this now is to
873         * link the attributes directly to the section key (there are new methods
874         * for this, starting from version 1.0.3).  
875         * 
876         * @param section  the section index.
877         * 
878         * @return The key.
879         *
880         * @since 1.0.3
881         */
882        protected Comparable getSectionKey(int section) {
883            Comparable key = null;
884            if (this.dataset != null) {
885                if (section >= 0 && section < this.dataset.getItemCount()) {
886                    key = this.dataset.getKey(section);
887                }
888            }
889            if (key == null) {
890                key = new Integer(section);
891            }
892            return key;
893        }
894        
895        /**
896         * Returns the paint associated with the specified key, or 
897         * <code>null</code> if there is no paint associated with the key.
898         * 
899         * @param key  the key (<code>null</code> not permitted).
900         * 
901         * @return The paint associated with the specified key, or 
902         *     <code>null</code>.
903         *     
904         * @throws IllegalArgumentException if <code>key</code> is 
905         *     <code>null</code>.
906         * 
907         * @see #setSectionPaint(Comparable, Paint)
908         * 
909         * @since 1.0.3
910         */
911        public Paint getSectionPaint(Comparable key) {
912            // null argument check delegated...
913            return this.sectionPaintMap.getPaint(key);
914        }
915        
916        /**
917         * Sets the paint associated with the specified key, and sends a 
918         * {@link PlotChangeEvent} to all registered listeners.
919         * 
920         * @param key  the key (<code>null</code> not permitted).
921         * @param paint  the paint.
922         * 
923         * @throws IllegalArgumentException if <code>key</code> is 
924         *     <code>null</code>.
925         *     
926         * @see #getSectionPaint(Comparable)
927         * 
928         * @since 1.0.3
929         */
930        public void setSectionPaint(Comparable key, Paint paint) {
931            // null argument check delegated...
932            this.sectionPaintMap.put(key, paint);
933            notifyListeners(new PlotChangeEvent(this));
934        }
935        
936        /**
937         * Returns the base section paint.  This is used when no other paint is 
938         * defined, which is rare.  The default value is <code>Color.gray</code>.
939         * 
940         * @return The paint (never <code>null</code>).
941         * 
942         * @see #setBaseSectionPaint(Paint)
943         */
944        public Paint getBaseSectionPaint() {
945            return this.baseSectionPaint;   
946        }
947        
948        /**
949         * Sets the base section paint and sends a {@link PlotChangeEvent} to all
950         * registered listeners.
951         * 
952         * @param paint  the paint (<code>null</code> not permitted).
953         * 
954         * @see #getBaseSectionPaint()
955         */
956        public void setBaseSectionPaint(Paint paint) {
957            if (paint == null) {
958                throw new IllegalArgumentException("Null 'paint' argument.");   
959            }
960            this.baseSectionPaint = paint;
961            notifyListeners(new PlotChangeEvent(this));
962        }
963        
964        //// SECTION OUTLINE PAINT ////////////////////////////////////////////////
965    
966        /**
967         * Returns the flag that controls whether or not the outline is drawn for
968         * each pie section.
969         * 
970         * @return The flag that controls whether or not the outline is drawn for
971         *         each pie section.
972         *         
973         * @see #setSectionOutlinesVisible(boolean)
974         */
975        public boolean getSectionOutlinesVisible() {
976            return this.sectionOutlinesVisible;
977        }
978        
979        /**
980         * Sets the flag that controls whether or not the outline is drawn for 
981         * each pie section, and sends a {@link PlotChangeEvent} to all registered
982         * listeners.
983         * 
984         * @param visible  the flag.
985         * 
986         * @see #getSectionOutlinesVisible()
987         */
988        public void setSectionOutlinesVisible(boolean visible) {
989            this.sectionOutlinesVisible = visible;
990            notifyListeners(new PlotChangeEvent(this));
991        }
992    
993        /**
994         * Returns the outline paint for the specified section.  This is equivalent 
995         * to <code>lookupSectionPaint(section, false)</code>.
996         * 
997         * @param key  the section key.
998         * 
999         * @return The paint for the specified section.
1000         * 
1001         * @since 1.0.3
1002         * 
1003         * @see #lookupSectionOutlinePaint(Comparable, boolean)
1004         */
1005        protected Paint lookupSectionOutlinePaint(Comparable key) {
1006            return lookupSectionOutlinePaint(key, false);        
1007        }
1008        
1009        /**
1010         * Returns the outline paint for the specified section.  The lookup 
1011         * involves these steps:
1012         * <ul>
1013         * <li>if {@link #getSectionOutlinePaint()} is non-<code>null</code>, 
1014         *         return it;</li>
1015         * <li>otherwise, if {@link #getSectionOutlinePaint(int)} is 
1016         *         non-<code>null</code> return it;</li>
1017         * <li>if {@link #getSectionOutlinePaint(int)} is <code>null</code> but 
1018         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1019         *         a new outline paint from the drawing supplier 
1020         *         ({@link #getDrawingSupplier()});
1021         * <li>if all else fails, return {@link #getBaseSectionOutlinePaint()}.
1022         * </ul> 
1023         * 
1024         * @param key  the section key.
1025         * @param autoPopulate  a flag that controls whether the drawing supplier 
1026         *     is used to auto-populate the section outline paint settings.
1027         *     
1028         * @return The paint.
1029         * 
1030         * @since 1.0.3
1031         */
1032        protected Paint lookupSectionOutlinePaint(Comparable key, 
1033                boolean autoPopulate) {
1034            
1035            // is there an override?
1036            Paint result = getSectionOutlinePaint();
1037            if (result != null) {
1038                return result;
1039            }
1040            
1041            // if not, check if there is a paint defined for the specified key
1042            result = this.sectionOutlinePaintMap.getPaint(key);
1043            if (result != null) {
1044                return result;
1045            }
1046            
1047            // nothing defined - do we autoPopulate?
1048            if (autoPopulate) {
1049                DrawingSupplier ds = getDrawingSupplier();
1050                if (ds != null) {
1051                    result = ds.getNextOutlinePaint();
1052                    this.sectionOutlinePaintMap.put(key, result);
1053                }
1054                else {
1055                    result = this.baseSectionOutlinePaint;
1056                }
1057            }
1058            else {
1059                result = this.baseSectionOutlinePaint;
1060            }
1061            return result;
1062        }
1063        
1064        /**
1065         * Returns the outline paint for ALL sections in the plot.
1066         *
1067         * @return The paint (possibly <code>null</code>).
1068         * 
1069         * @see #setSectionOutlinePaint(Paint)
1070         * 
1071         * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} and 
1072         *     {@link #getBaseSectionOutlinePaint()}.  Deprecated as of version 
1073         *     1.0.6.
1074         */
1075        public Paint getSectionOutlinePaint() {
1076            return this.sectionOutlinePaint;
1077        }
1078    
1079        /**
1080         * Sets the outline paint for ALL sections in the plot.  If this is set to
1081         * </code>null</code>, then a list of paints is used instead (to allow
1082         * different colors to be used for each section).
1083         *
1084         * @param paint  the paint (<code>null</code> permitted).
1085         * 
1086         * @see #getSectionOutlinePaint()
1087         * 
1088         * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)} and 
1089         *     {@link #setBaseSectionOutlinePaint(Paint)}.  Deprecated as of 
1090         *     version 1.0.6.
1091         */
1092        public void setSectionOutlinePaint(Paint paint) {
1093            this.sectionOutlinePaint = paint;
1094            notifyListeners(new PlotChangeEvent(this));
1095        }
1096    
1097        /**
1098         * Returns the outline paint associated with the specified key, or 
1099         * <code>null</code> if there is no paint associated with the key.
1100         * 
1101         * @param key  the key (<code>null</code> not permitted).
1102         * 
1103         * @return The paint associated with the specified key, or 
1104         *     <code>null</code>.
1105         *     
1106         * @throws IllegalArgumentException if <code>key</code> is 
1107         *     <code>null</code>.
1108         * 
1109         * @see #setSectionOutlinePaint(Comparable, Paint)
1110         * 
1111         * @since 1.0.3
1112         */
1113        public Paint getSectionOutlinePaint(Comparable key) {
1114            // null argument check delegated...
1115            return this.sectionOutlinePaintMap.getPaint(key);
1116        }
1117        
1118        /**
1119         * Sets the outline paint associated with the specified key, and sends a 
1120         * {@link PlotChangeEvent} to all registered listeners.
1121         * 
1122         * @param key  the key (<code>null</code> not permitted).
1123         * @param paint  the paint.
1124         * 
1125         * @throws IllegalArgumentException if <code>key</code> is 
1126         *     <code>null</code>.
1127         *     
1128         * @see #getSectionOutlinePaint(Comparable)
1129         * 
1130         * @since 1.0.3
1131         */
1132        public void setSectionOutlinePaint(Comparable key, Paint paint) {
1133            // null argument check delegated...
1134            this.sectionOutlinePaintMap.put(key, paint);
1135            notifyListeners(new PlotChangeEvent(this));
1136        }
1137        
1138        /**
1139         * Returns the base section paint.  This is used when no other paint is 
1140         * available.
1141         * 
1142         * @return The paint (never <code>null</code>).
1143         * 
1144         * @see #setBaseSectionOutlinePaint(Paint)
1145         */
1146        public Paint getBaseSectionOutlinePaint() {
1147            return this.baseSectionOutlinePaint;   
1148        }
1149        
1150        /**
1151         * Sets the base section paint.
1152         * 
1153         * @param paint  the paint (<code>null</code> not permitted).
1154         * 
1155         * @see #getBaseSectionOutlinePaint()
1156         */
1157        public void setBaseSectionOutlinePaint(Paint paint) {
1158            if (paint == null) {
1159                throw new IllegalArgumentException("Null 'paint' argument.");   
1160            }
1161            this.baseSectionOutlinePaint = paint;
1162            notifyListeners(new PlotChangeEvent(this));
1163        }
1164        
1165        //// SECTION OUTLINE STROKE ///////////////////////////////////////////////
1166    
1167        /**
1168         * Returns the outline stroke for the specified section.  This is equivalent 
1169         * to <code>lookupSectionOutlineStroke(section, false)</code>.
1170         * 
1171         * @param key  the section key.
1172         * 
1173         * @return The stroke for the specified section.
1174         * 
1175         * @since 1.0.3
1176         * 
1177         * @see #lookupSectionOutlineStroke(Comparable, boolean)
1178         */
1179        protected Stroke lookupSectionOutlineStroke(Comparable key) {
1180            return lookupSectionOutlineStroke(key, false);        
1181        }
1182        
1183        /**
1184         * Returns the outline stroke for the specified section.  The lookup 
1185         * involves these steps:
1186         * <ul>
1187         * <li>if {@link #getSectionOutlineStroke()} is non-<code>null</code>, 
1188         *         return it;</li>
1189         * <li>otherwise, if {@link #getSectionOutlineStroke(int)} is 
1190         *         non-<code>null</code> return it;</li>
1191         * <li>if {@link #getSectionOutlineStroke(int)} is <code>null</code> but 
1192         *         <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1193         *         a new outline stroke from the drawing supplier 
1194         *         ({@link #getDrawingSupplier()});
1195         * <li>if all else fails, return {@link #getBaseSectionOutlineStroke()}.
1196         * </ul> 
1197         * 
1198         * @param key  the section key.
1199         * @param autoPopulate  a flag that controls whether the drawing supplier 
1200         *     is used to auto-populate the section outline stroke settings.
1201         *     
1202         * @return The stroke.
1203         * 
1204         * @since 1.0.3
1205         */
1206        protected Stroke lookupSectionOutlineStroke(Comparable key, 
1207                boolean autoPopulate) {
1208            
1209            // is there an override?
1210            Stroke result = getSectionOutlineStroke();
1211            if (result != null) {
1212                return result;
1213            }
1214            
1215            // if not, check if there is a stroke defined for the specified key
1216            result = this.sectionOutlineStrokeMap.getStroke(key);
1217            if (result != null) {
1218                return result;
1219            }
1220            
1221            // nothing defined - do we autoPopulate?
1222            if (autoPopulate) {
1223                DrawingSupplier ds = getDrawingSupplier();
1224                if (ds != null) {
1225                    result = ds.getNextOutlineStroke();
1226                    this.sectionOutlineStrokeMap.put(key, result);
1227                }
1228                else {
1229                    result = this.baseSectionOutlineStroke;
1230                }
1231            }
1232            else {
1233                result = this.baseSectionOutlineStroke;
1234            }
1235            return result;
1236        }
1237        
1238        /**
1239         * Returns the outline stroke for ALL sections in the plot.
1240         *
1241         * @return The stroke (possibly <code>null</code>).
1242         * 
1243         * @see #setSectionOutlineStroke(Stroke)
1244         * 
1245         * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} and 
1246         *     {@link #getBaseSectionOutlineStroke()}.  Deprecated as of version 
1247         *     1.0.6.
1248         */
1249        public Stroke getSectionOutlineStroke() {
1250            return this.sectionOutlineStroke;
1251        }
1252    
1253        /**
1254         * Sets the outline stroke for ALL sections in the plot.  If this is set to
1255         * </code>null</code>, then a list of paints is used instead (to allow
1256         * different colors to be used for each section).
1257         *
1258         * @param stroke  the stroke (<code>null</code> permitted).
1259         * 
1260         * @see #getSectionOutlineStroke()
1261         * 
1262         * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} and 
1263         *     {@link #setBaseSectionOutlineStroke(Stroke)}.  Deprecated as of 
1264         *     version 1.0.6.
1265         */
1266        public void setSectionOutlineStroke(Stroke stroke) {
1267            this.sectionOutlineStroke = stroke;
1268            notifyListeners(new PlotChangeEvent(this));
1269        }
1270    
1271        /**
1272         * Returns the outline stroke associated with the specified key, or 
1273         * <code>null</code> if there is no stroke associated with the key.
1274         * 
1275         * @param key  the key (<code>null</code> not permitted).
1276         * 
1277         * @return The stroke associated with the specified key, or 
1278         *     <code>null</code>.
1279         *     
1280         * @throws IllegalArgumentException if <code>key</code> is 
1281         *     <code>null</code>.
1282         * 
1283         * @see #setSectionOutlineStroke(Comparable, Stroke)
1284         * 
1285         * @since 1.0.3
1286         */
1287        public Stroke getSectionOutlineStroke(Comparable key) {
1288            // null argument check delegated...
1289            return this.sectionOutlineStrokeMap.getStroke(key);
1290        }
1291        
1292        /**
1293         * Sets the outline stroke associated with the specified key, and sends a 
1294         * {@link PlotChangeEvent} to all registered listeners.
1295         * 
1296         * @param key  the key (<code>null</code> not permitted).
1297         * @param stroke  the stroke.
1298         * 
1299         * @throws IllegalArgumentException if <code>key</code> is 
1300         *     <code>null</code>.
1301         *     
1302         * @see #getSectionOutlineStroke(Comparable)
1303         * 
1304         * @since 1.0.3
1305         */
1306        public void setSectionOutlineStroke(Comparable key, Stroke stroke) {
1307            // null argument check delegated...
1308            this.sectionOutlineStrokeMap.put(key, stroke);
1309            notifyListeners(new PlotChangeEvent(this));
1310        }
1311        
1312        /**
1313         * Returns the base section stroke.  This is used when no other stroke is 
1314         * available.
1315         * 
1316         * @return The stroke (never <code>null</code>).
1317         * 
1318         * @see #setBaseSectionOutlineStroke(Stroke)
1319         */
1320        public Stroke getBaseSectionOutlineStroke() {
1321            return this.baseSectionOutlineStroke;   
1322        }
1323        
1324        /**
1325         * Sets the base section stroke.
1326         * 
1327         * @param stroke  the stroke (<code>null</code> not permitted).
1328         * 
1329         * @see #getBaseSectionOutlineStroke()
1330         */
1331        public void setBaseSectionOutlineStroke(Stroke stroke) {
1332            if (stroke == null) {
1333                throw new IllegalArgumentException("Null 'stroke' argument.");   
1334            }
1335            this.baseSectionOutlineStroke = stroke;
1336            notifyListeners(new PlotChangeEvent(this));
1337        }
1338    
1339        /**
1340         * Returns the shadow paint.
1341         * 
1342         * @return The paint (possibly <code>null</code>).
1343         * 
1344         * @see #setShadowPaint(Paint)
1345         */
1346        public Paint getShadowPaint() {
1347            return this.shadowPaint;   
1348        }
1349        
1350        /**
1351         * Sets the shadow paint and sends a {@link PlotChangeEvent} to all 
1352         * registered listeners.
1353         * 
1354         * @param paint  the paint (<code>null</code> permitted).
1355         * 
1356         * @see #getShadowPaint()
1357         */
1358        public void setShadowPaint(Paint paint) {
1359            this.shadowPaint = paint;
1360            notifyListeners(new PlotChangeEvent(this));
1361        }
1362        
1363        /**
1364         * Returns the x-offset for the shadow effect.
1365         * 
1366         * @return The offset (in Java2D units).
1367         * 
1368         * @see #setShadowXOffset(double)
1369         */
1370        public double getShadowXOffset() {
1371            return this.shadowXOffset;
1372        }
1373        
1374        /**
1375         * Sets the x-offset for the shadow effect and sends a 
1376         * {@link PlotChangeEvent} to all registered listeners.
1377         * 
1378         * @param offset  the offset (in Java2D units).
1379         * 
1380         * @see #getShadowXOffset()
1381         */
1382        public void setShadowXOffset(double offset) {
1383            this.shadowXOffset = offset;   
1384            notifyListeners(new PlotChangeEvent(this));
1385        }
1386        
1387        /**
1388         * Returns the y-offset for the shadow effect.
1389         * 
1390         * @return The offset (in Java2D units).
1391         * 
1392         * @see #setShadowYOffset(double)
1393         */
1394        public double getShadowYOffset() {
1395            return this.shadowYOffset;
1396        }
1397        
1398        /**
1399         * Sets the y-offset for the shadow effect and sends a 
1400         * {@link PlotChangeEvent} to all registered listeners.
1401         * 
1402         * @param offset  the offset (in Java2D units).
1403         * 
1404         * @see #getShadowYOffset()
1405         */
1406        public void setShadowYOffset(double offset) {
1407            this.shadowYOffset = offset;   
1408            notifyListeners(new PlotChangeEvent(this));
1409        }
1410        
1411        /**
1412         * Returns the amount that the section with the specified key should be
1413         * exploded.
1414         * 
1415