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