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