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