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