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