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