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