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