Source for org.jfree.chart.plot.PiePlot

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