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