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