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