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