0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ------------
0028: * PiePlot.java
0029: * ------------
0030: * (C) Copyright 2000-2007, by Andrzej Porebski and Contributors.
0031: *
0032: * Original Author: Andrzej Porebski;
0033: * Contributor(s): David Gilbert (for Object Refinery Limited);
0034: * Martin Cordova (percentages in labels);
0035: * Richard Atkinson (URL support for image maps);
0036: * Christian W. Zuckschwerdt;
0037: * Arnaud Lelievre;
0038: * Andreas Schroeder (very minor);
0039: *
0040: * $Id: PiePlot.java,v 1.17.2.25 2007/06/14 15:04:21 mungady Exp $
0041: *
0042: * Changes (from 21-Jun-2001)
0043: * --------------------------
0044: * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
0045: * 18-Sep-2001 : Updated header (DG);
0046: * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
0047: * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to
0048: * Plot.java (DG);
0049: * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
0050: * 13-Nov-2001 : Modified plot subclasses so that null axes are possible for
0051: * pie plot (DG);
0052: * 17-Nov-2001 : Added PieDataset interface and amended this class accordingly,
0053: * and completed removal of BlankAxis class as it is no longer
0054: * required (DG);
0055: * 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG);
0056: * 21-Nov-2001 : Added options for exploding pie sections and filled out range
0057: * of properties (DG);
0058: * Added option for percentages in chart labels, based on code
0059: * by Martin Cordova (DG);
0060: * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
0061: * 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG);
0062: * 13-Dec-2001 : Added tooltips (DG);
0063: * 16-Jan-2002 : Renamed tooltips class (DG);
0064: * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
0065: * 05-Feb-2002 : Added alpha-transparency to plot class, and updated
0066: * constructors accordingly (DG);
0067: * 06-Feb-2002 : Added optional background image and alpha-transparency to Plot
0068: * and subclasses. Clipped drawing within plot area (DG);
0069: * 26-Mar-2002 : Added an empty zoom method (DG);
0070: * 18-Apr-2002 : PieDataset is no longer sorted (oldman);
0071: * 23-Apr-2002 : Moved dataset from JFreeChart to Plot. Added
0072: * getLegendItemLabels() method (DG);
0073: * 19-Jun-2002 : Added attributes to control starting angle and direction
0074: * (default is now clockwise) (DG);
0075: * 25-Jun-2002 : Removed redundant imports (DG);
0076: * 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG);
0077: * 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG);
0078: * 30-Jul-2002 : Moved summation code to DatasetUtilities (DG);
0079: * 05-Aug-2002 : Added URL support for image maps - new member variable for
0080: * urlGenerator, modified constructor and minor change to the
0081: * draw method (RA);
0082: * 18-Sep-2002 : Modified the percent label creation and added setters for the
0083: * formatters (AS);
0084: * 24-Sep-2002 : Added getLegendItems() method (DG);
0085: * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
0086: * 09-Oct-2002 : Added check for null entity collection (DG);
0087: * 30-Oct-2002 : Changed PieDataset interface (DG);
0088: * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
0089: * 02-Jan-2003 : Fixed "no data" message (DG);
0090: * 23-Jan-2003 : Modified to extract data from rows OR columns in
0091: * CategoryDataset (DG);
0092: * 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply
0093: * (bug id 685536) (DG);
0094: * 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip
0095: * and URL generators (DG);
0096: * 21-Mar-2003 : Added a minimum angle for drawing arcs
0097: * (see bug id 620031) (DG);
0098: * 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG);
0099: * 02-Jun-2003 : Fixed bug 721733 (DG);
0100: * 30-Jul-2003 : Modified entity constructor (CZ);
0101: * 19-Aug-2003 : Implemented Cloneable (DG);
0102: * 29-Aug-2003 : Fixed bug 796936 (null pointer on setOutlinePaint()) (DG);
0103: * 08-Sep-2003 : Added internationalization via use of properties
0104: * resourceBundle (RFE 690236) (AL);
0105: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0106: * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
0107: * 05-Nov-2003 : Fixed missing legend bug (DG);
0108: * 10-Nov-2003 : Re-added the DatasetChangeListener to constructors (CZ);
0109: * 29-Jan-2004 : Fixed clipping bug in draw() method (DG);
0110: * 11-Mar-2004 : Major overhaul to improve labelling (DG);
0111: * 31-Mar-2004 : Made an adjustment for the plot area when the label generator
0112: * is null. Fixed null pointer exception when the label
0113: * generator returns null for a label (DG);
0114: * 06-Apr-2004 : Added getter, setter, serialization and draw support for
0115: * labelBackgroundPaint (AS);
0116: * 08-Apr-2004 : Added flag to control whether null values are ignored or
0117: * not (DG);
0118: * 15-Apr-2004 : Fixed some minor warnings from Eclipse (DG);
0119: * 26-Apr-2004 : Added attributes for label outline and shadow (DG);
0120: * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
0121: * 04-Nov-2004 : Fixed null pointer exception with new LegendTitle class (DG);
0122: * 09-Nov-2004 : Added user definable legend item shape (DG);
0123: * 25-Nov-2004 : Added new legend label generator (DG);
0124: * 20-Apr-2005 : Added a tool tip generator for legend labels (DG);
0125: * 26-Apr-2005 : Removed LOGGER (DG);
0126: * 05-May-2005 : Updated draw() method parameters (DG);
0127: * 10-May-2005 : Added flag to control visibility of label linking lines, plus
0128: * another flag to control the handling of zero values (DG);
0129: * 08-Jun-2005 : Fixed bug in getLegendItems() method (not respecting flags
0130: * for ignoring null and zero values), and fixed equals() method
0131: * to handle GradientPaint (DG);
0132: * 15-Jul-2005 : Added sectionOutlinesVisible attribute (DG);
0133: * ------------- JFREECHART 1.0.x ---------------------------------------------
0134: * 09-Jan-2006 : Fixed bug 1400442, inconsistent treatment of null and zero
0135: * values in dataset (DG);
0136: * 28-Feb-2006 : Fixed bug 1440415, bad distribution of pie section
0137: * labels (DG);
0138: * 27-Sep-2006 : Initialised baseSectionPaint correctly, added lookup methods
0139: * for section paint, outline paint and outline stroke (DG);
0140: * 27-Sep-2006 : Refactored paint and stroke methods to use keys rather than
0141: * section indices (DG);
0142: * 03-Oct-2006 : Replaced call to JRE 1.5 method (DG);
0143: * 23-Nov-2006 : Added support for URLs for the legend items (DG);
0144: * 24-Nov-2006 : Cloning fixes (DG);
0145: * 17-Apr-2007 : Check for null label in legend items (DG);
0146: * 19-Apr-2007 : Deprecated override settings (DG);
0147: * 18-May-2007 : Set dataset for LegendItem (DG);
0148: * 14-Jun-2007 : Added label distributor attribute (DG);
0149: *
0150: */
0151:
0152: package org.jfree.chart.plot;
0153:
0154: import java.awt.AlphaComposite;
0155: import java.awt.BasicStroke;
0156: import java.awt.Color;
0157: import java.awt.Composite;
0158: import java.awt.Font;
0159: import java.awt.Graphics2D;
0160: import java.awt.Paint;
0161: import java.awt.Shape;
0162: import java.awt.Stroke;
0163: import java.awt.geom.Arc2D;
0164: import java.awt.geom.Line2D;
0165: import java.awt.geom.Point2D;
0166: import java.awt.geom.Rectangle2D;
0167: import java.io.IOException;
0168: import java.io.ObjectInputStream;
0169: import java.io.ObjectOutputStream;
0170: import java.io.Serializable;
0171: import java.util.Iterator;
0172: import java.util.List;
0173: import java.util.Map;
0174: import java.util.ResourceBundle;
0175: import java.util.TreeMap;
0176:
0177: import org.jfree.chart.LegendItem;
0178: import org.jfree.chart.LegendItemCollection;
0179: import org.jfree.chart.PaintMap;
0180: import org.jfree.chart.StrokeMap;
0181: import org.jfree.chart.entity.EntityCollection;
0182: import org.jfree.chart.entity.PieSectionEntity;
0183: import org.jfree.chart.event.PlotChangeEvent;
0184: import org.jfree.chart.labels.PieSectionLabelGenerator;
0185: import org.jfree.chart.labels.PieToolTipGenerator;
0186: import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
0187: import org.jfree.chart.urls.PieURLGenerator;
0188: import org.jfree.data.DefaultKeyedValues;
0189: import org.jfree.data.KeyedValues;
0190: import org.jfree.data.general.DatasetChangeEvent;
0191: import org.jfree.data.general.DatasetUtilities;
0192: import org.jfree.data.general.PieDataset;
0193: import org.jfree.io.SerialUtilities;
0194: import org.jfree.text.G2TextMeasurer;
0195: import org.jfree.text.TextBlock;
0196: import org.jfree.text.TextBox;
0197: import org.jfree.text.TextUtilities;
0198: import org.jfree.ui.RectangleAnchor;
0199: import org.jfree.ui.RectangleInsets;
0200: import org.jfree.util.ObjectUtilities;
0201: import org.jfree.util.PaintUtilities;
0202: import org.jfree.util.PublicCloneable;
0203: import org.jfree.util.Rotation;
0204: import org.jfree.util.ShapeUtilities;
0205:
0206: /**
0207: * A plot that displays data in the form of a pie chart, using data from any
0208: * class that implements the {@link PieDataset} interface.
0209: * <P>
0210: * Special notes:
0211: * <ol>
0212: * <li>the default starting point is 12 o'clock and the pie sections proceed
0213: * in a clockwise direction, but these settings can be changed;</li>
0214: * <li>negative values in the dataset are ignored;</li>
0215: * <li>there are utility methods for creating a {@link PieDataset} from a
0216: * {@link org.jfree.data.category.CategoryDataset};</li>
0217: * </ol>
0218: *
0219: * @see Plot
0220: * @see PieDataset
0221: */
0222: public class PiePlot extends Plot implements Cloneable, Serializable {
0223:
0224: /** For serialization. */
0225: private static final long serialVersionUID = -795612466005590431L;
0226:
0227: /** The default interior gap. */
0228: public static final double DEFAULT_INTERIOR_GAP = 0.25;
0229:
0230: /** The maximum interior gap (currently 40%). */
0231: public static final double MAX_INTERIOR_GAP = 0.40;
0232:
0233: /** The default starting angle for the pie chart. */
0234: public static final double DEFAULT_START_ANGLE = 90.0;
0235:
0236: /** The default section label font. */
0237: public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
0238: Font.PLAIN, 10);
0239:
0240: /** The default section label paint. */
0241: public static final Paint DEFAULT_LABEL_PAINT = Color.black;
0242:
0243: /** The default section label background paint. */
0244: public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(
0245: 255, 255, 192);
0246:
0247: /** The default section label outline paint. */
0248: public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black;
0249:
0250: /** The default section label outline stroke. */
0251: public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(
0252: 0.5f);
0253:
0254: /** The default section label shadow paint. */
0255: public static final Paint DEFAULT_LABEL_SHADOW_PAINT = Color.lightGray;
0256:
0257: /** The default minimum arc angle to draw. */
0258: public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;
0259:
0260: /** The dataset for the pie chart. */
0261: private PieDataset dataset;
0262:
0263: /** The pie index (used by the {@link MultiplePiePlot} class). */
0264: private int pieIndex;
0265:
0266: /**
0267: * The amount of space left around the outside of the pie plot, expressed
0268: * as a percentage.
0269: */
0270: private double interiorGap;
0271:
0272: /** Flag determining whether to draw an ellipse or a perfect circle. */
0273: private boolean circular;
0274:
0275: /** The starting angle. */
0276: private double startAngle;
0277:
0278: /** The direction for the pie segments. */
0279: private Rotation direction;
0280:
0281: /**
0282: * The paint for ALL sections (overrides list).
0283: *
0284: * @deprecated This field is redundant, it is sufficient to use
0285: * sectionPaintMap and baseSectionPaint. Deprecated as of version
0286: * 1.0.6.
0287: */
0288: private transient Paint sectionPaint;
0289:
0290: /** The section paint map. */
0291: private PaintMap sectionPaintMap;
0292:
0293: /** The base section paint (fallback). */
0294: private transient Paint baseSectionPaint;
0295:
0296: /**
0297: * A flag that controls whether or not an outline is drawn for each
0298: * section in the plot.
0299: */
0300: private boolean sectionOutlinesVisible;
0301:
0302: /**
0303: * The outline paint for ALL sections (overrides list).
0304: *
0305: * @deprecated This field is redundant, it is sufficient to use
0306: * sectionOutlinePaintMap and baseSectionOutlinePaint. Deprecated as
0307: * of version 1.0.6.
0308: */
0309: private transient Paint sectionOutlinePaint;
0310:
0311: /** The section outline paint map. */
0312: private PaintMap sectionOutlinePaintMap;
0313:
0314: /** The base section outline paint (fallback). */
0315: private transient Paint baseSectionOutlinePaint;
0316:
0317: /**
0318: * The outline stroke for ALL sections (overrides list).
0319: *
0320: * @deprecated This field is redundant, it is sufficient to use
0321: * sectionOutlineStrokeMap and baseSectionOutlineStroke. Deprecated as
0322: * of version 1.0.6.
0323: */
0324: private transient Stroke sectionOutlineStroke;
0325:
0326: /** The section outline stroke map. */
0327: private StrokeMap sectionOutlineStrokeMap;
0328:
0329: /** The base section outline stroke (fallback). */
0330: private transient Stroke baseSectionOutlineStroke;
0331:
0332: /** The shadow paint. */
0333: private transient Paint shadowPaint = Color.gray;
0334:
0335: /** The x-offset for the shadow effect. */
0336: private double shadowXOffset = 4.0f;
0337:
0338: /** The y-offset for the shadow effect. */
0339: private double shadowYOffset = 4.0f;
0340:
0341: /** The percentage amount to explode each pie section. */
0342: private Map explodePercentages;
0343:
0344: /** The section label generator. */
0345: private PieSectionLabelGenerator labelGenerator;
0346:
0347: /** The font used to display the section labels. */
0348: private Font labelFont;
0349:
0350: /** The color used to draw the section labels. */
0351: private transient Paint labelPaint;
0352:
0353: /** The color used to draw the background of the section labels. */
0354: private transient Paint labelBackgroundPaint;
0355:
0356: /**
0357: * The paint used to draw the outline of the section labels
0358: * (<code>null</code> permitted).
0359: */
0360: private transient Paint labelOutlinePaint;
0361:
0362: /**
0363: * The stroke used to draw the outline of the section labels
0364: * (<code>null</code> permitted).
0365: */
0366: private transient Stroke labelOutlineStroke;
0367:
0368: /**
0369: * The paint used to draw the shadow for the section labels
0370: * (<code>null</code> permitted).
0371: */
0372: private transient Paint labelShadowPaint;
0373:
0374: /** The maximum label width as a percentage of the plot width. */
0375: private double maximumLabelWidth = 0.20;
0376:
0377: /**
0378: * The gap between the labels and the plot as a percentage of the plot
0379: * width.
0380: */
0381: private double labelGap = 0.05;
0382:
0383: /** A flag that controls whether or not the label links are drawn. */
0384: private boolean labelLinksVisible;
0385:
0386: /** The link margin. */
0387: private double labelLinkMargin = 0.05;
0388:
0389: /** The paint used for the label linking lines. */
0390: private transient Paint labelLinkPaint = Color.black;
0391:
0392: /** The stroke used for the label linking lines. */
0393: private transient Stroke labelLinkStroke = new BasicStroke(0.5f);
0394:
0395: /**
0396: * The pie section label distributor.
0397: *
0398: * @since 1.0.6
0399: */
0400: private AbstractPieLabelDistributor labelDistributor;
0401:
0402: /** The tooltip generator. */
0403: private PieToolTipGenerator toolTipGenerator;
0404:
0405: /** The URL generator. */
0406: private PieURLGenerator urlGenerator;
0407:
0408: /** The legend label generator. */
0409: private PieSectionLabelGenerator legendLabelGenerator;
0410:
0411: /** A tool tip generator for the legend. */
0412: private PieSectionLabelGenerator legendLabelToolTipGenerator;
0413:
0414: /**
0415: * A URL generator for the legend items (optional).
0416: *
0417: * @since 1.0.4.
0418: */
0419: private PieURLGenerator legendLabelURLGenerator;
0420:
0421: /**
0422: * A flag that controls whether <code>null</code> values are ignored.
0423: */
0424: private boolean ignoreNullValues;
0425:
0426: /**
0427: * A flag that controls whether zero values are ignored.
0428: */
0429: private boolean ignoreZeroValues;
0430:
0431: /** The legend item shape. */
0432: private transient Shape legendItemShape;
0433:
0434: /**
0435: * The smallest arc angle that will get drawn (this is to avoid a bug in
0436: * various Java implementations that causes the JVM to crash). See this
0437: * link for details:
0438: *
0439: * http://www.jfree.org/phpBB2/viewtopic.php?t=2707
0440: *
0441: * ...and this bug report in the Java Bug Parade:
0442: *
0443: * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html
0444: */
0445: private double minimumArcAngleToDraw;
0446:
0447: /** The resourceBundle for the localization. */
0448: protected static ResourceBundle localizationResources = ResourceBundle
0449: .getBundle("org.jfree.chart.plot.LocalizationBundle");
0450:
0451: /**
0452: * Creates a new plot. The dataset is initially set to <code>null</code>.
0453: */
0454: public PiePlot() {
0455: this (null);
0456: }
0457:
0458: /**
0459: * Creates a plot that will draw a pie chart for the specified dataset.
0460: *
0461: * @param dataset the dataset (<code>null</code> permitted).
0462: */
0463: public PiePlot(PieDataset dataset) {
0464: super ();
0465: this .dataset = dataset;
0466: if (dataset != null) {
0467: dataset.addChangeListener(this );
0468: }
0469: this .pieIndex = 0;
0470:
0471: this .interiorGap = DEFAULT_INTERIOR_GAP;
0472: this .circular = true;
0473: this .startAngle = DEFAULT_START_ANGLE;
0474: this .direction = Rotation.CLOCKWISE;
0475: this .minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;
0476:
0477: this .sectionPaint = null;
0478: this .sectionPaintMap = new PaintMap();
0479: this .baseSectionPaint = Color.gray;
0480:
0481: this .sectionOutlinesVisible = true;
0482: this .sectionOutlinePaint = null;
0483: this .sectionOutlinePaintMap = new PaintMap();
0484: this .baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;
0485:
0486: this .sectionOutlineStroke = null;
0487: this .sectionOutlineStrokeMap = new StrokeMap();
0488: this .baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;
0489:
0490: this .explodePercentages = new TreeMap();
0491:
0492: this .labelGenerator = new StandardPieSectionLabelGenerator();
0493: this .labelFont = DEFAULT_LABEL_FONT;
0494: this .labelPaint = DEFAULT_LABEL_PAINT;
0495: this .labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
0496: this .labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
0497: this .labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
0498: this .labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
0499: this .labelLinksVisible = true;
0500: this .labelDistributor = new PieLabelDistributor(0);
0501:
0502: this .toolTipGenerator = null;
0503: this .urlGenerator = null;
0504: this .legendLabelGenerator = new StandardPieSectionLabelGenerator();
0505: this .legendLabelToolTipGenerator = null;
0506: this .legendLabelURLGenerator = null;
0507: this .legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;
0508:
0509: this .ignoreNullValues = false;
0510: this .ignoreZeroValues = false;
0511: }
0512:
0513: /**
0514: * Returns the dataset.
0515: *
0516: * @return The dataset (possibly <code>null</code>).
0517: *
0518: * @see #setDataset(PieDataset)
0519: */
0520: public PieDataset getDataset() {
0521: return this .dataset;
0522: }
0523:
0524: /**
0525: * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'.
0526: *
0527: * @param dataset the dataset (<code>null</code> permitted).
0528: *
0529: * @see #getDataset()
0530: */
0531: public void setDataset(PieDataset dataset) {
0532: // if there is an existing dataset, remove the plot from the list of
0533: // change listeners...
0534: PieDataset existing = this .dataset;
0535: if (existing != null) {
0536: existing.removeChangeListener(this );
0537: }
0538:
0539: // set the new dataset, and register the chart as a change listener...
0540: this .dataset = dataset;
0541: if (dataset != null) {
0542: setDatasetGroup(dataset.getGroup());
0543: dataset.addChangeListener(this );
0544: }
0545:
0546: // send a dataset change event to self...
0547: DatasetChangeEvent event = new DatasetChangeEvent(this , dataset);
0548: datasetChanged(event);
0549: }
0550:
0551: /**
0552: * Returns the pie index (this is used by the {@link MultiplePiePlot} class
0553: * to track subplots).
0554: *
0555: * @return The pie index.
0556: *
0557: * @see #setPieIndex(int)
0558: */
0559: public int getPieIndex() {
0560: return this .pieIndex;
0561: }
0562:
0563: /**
0564: * Sets the pie index (this is used by the {@link MultiplePiePlot} class to
0565: * track subplots).
0566: *
0567: * @param index the index.
0568: *
0569: * @see #getPieIndex()
0570: */
0571: public void setPieIndex(int index) {
0572: this .pieIndex = index;
0573: }
0574:
0575: /**
0576: * Returns the start angle for the first pie section. This is measured in
0577: * degrees starting from 3 o'clock and measuring anti-clockwise.
0578: *
0579: * @return The start angle.
0580: *
0581: * @see #setStartAngle(double)
0582: */
0583: public double getStartAngle() {
0584: return this .startAngle;
0585: }
0586:
0587: /**
0588: * Sets the starting angle and sends a {@link PlotChangeEvent} to all
0589: * registered listeners. The initial default value is 90 degrees, which
0590: * corresponds to 12 o'clock. A value of zero corresponds to 3 o'clock...
0591: * this is the encoding used by Java's Arc2D class.
0592: *
0593: * @param angle the angle (in degrees).
0594: *
0595: * @see #getStartAngle()
0596: */
0597: public void setStartAngle(double angle) {
0598: this .startAngle = angle;
0599: notifyListeners(new PlotChangeEvent(this ));
0600: }
0601:
0602: /**
0603: * Returns the direction in which the pie sections are drawn (clockwise or
0604: * anti-clockwise).
0605: *
0606: * @return The direction (never <code>null</code>).
0607: *
0608: * @see #setDirection(Rotation)
0609: */
0610: public Rotation getDirection() {
0611: return this .direction;
0612: }
0613:
0614: /**
0615: * Sets the direction in which the pie sections are drawn and sends a
0616: * {@link PlotChangeEvent} to all registered listeners.
0617: *
0618: * @param direction the direction (<code>null</code> not permitted).
0619: *
0620: * @see #getDirection()
0621: */
0622: public void setDirection(Rotation direction) {
0623: if (direction == null) {
0624: throw new IllegalArgumentException(
0625: "Null 'direction' argument.");
0626: }
0627: this .direction = direction;
0628: notifyListeners(new PlotChangeEvent(this ));
0629:
0630: }
0631:
0632: /**
0633: * Returns the interior gap, measured as a percentage of the available
0634: * drawing space.
0635: *
0636: * @return The gap (as a percentage of the available drawing space).
0637: *
0638: * @see #setInteriorGap(double)
0639: */
0640: public double getInteriorGap() {
0641: return this .interiorGap;
0642: }
0643:
0644: /**
0645: * Sets the interior gap and sends a {@link PlotChangeEvent} to all
0646: * registered listeners. This controls the space between the edges of the
0647: * pie plot and the plot area itself (the region where the section labels
0648: * appear).
0649: *
0650: * @param percent the gap (as a percentage of the available drawing space).
0651: *
0652: * @see #getInteriorGap()
0653: */
0654: public void setInteriorGap(double percent) {
0655:
0656: // check arguments...
0657: if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
0658: throw new IllegalArgumentException("Invalid 'percent' ("
0659: + percent + ") argument.");
0660: }
0661:
0662: // make the change...
0663: if (this .interiorGap != percent) {
0664: this .interiorGap = percent;
0665: notifyListeners(new PlotChangeEvent(this ));
0666: }
0667:
0668: }
0669:
0670: /**
0671: * Returns a flag indicating whether the pie chart is circular, or
0672: * stretched into an elliptical shape.
0673: *
0674: * @return A flag indicating whether the pie chart is circular.
0675: *
0676: * @see #setCircular(boolean)
0677: */
0678: public boolean isCircular() {
0679: return this .circular;
0680: }
0681:
0682: /**
0683: * A flag indicating whether the pie chart is circular, or stretched into
0684: * an elliptical shape.
0685: *
0686: * @param flag the new value.
0687: *
0688: * @see #isCircular()
0689: */
0690: public void setCircular(boolean flag) {
0691: setCircular(flag, true);
0692: }
0693:
0694: /**
0695: * Sets the circular attribute and, if requested, sends a
0696: * {@link PlotChangeEvent} to all registered listeners.
0697: *
0698: * @param circular the new value of the flag.
0699: * @param notify notify listeners?
0700: *
0701: * @see #isCircular()
0702: */
0703: public void setCircular(boolean circular, boolean notify) {
0704: this .circular = circular;
0705: if (notify) {
0706: notifyListeners(new PlotChangeEvent(this ));
0707: }
0708: }
0709:
0710: /**
0711: * Returns the flag that controls whether <code>null</code> values in the
0712: * dataset are ignored.
0713: *
0714: * @return A boolean.
0715: *
0716: * @see #setIgnoreNullValues(boolean)
0717: */
0718: public boolean getIgnoreNullValues() {
0719: return this .ignoreNullValues;
0720: }
0721:
0722: /**
0723: * Sets a flag that controls whether <code>null</code> values are ignored,
0724: * and sends a {@link PlotChangeEvent} to all registered listeners. At
0725: * present, this only affects whether or not the key is presented in the
0726: * legend.
0727: *
0728: * @param flag the flag.
0729: *
0730: * @see #getIgnoreNullValues()
0731: * @see #setIgnoreZeroValues(boolean)
0732: */
0733: public void setIgnoreNullValues(boolean flag) {
0734: this .ignoreNullValues = flag;
0735: notifyListeners(new PlotChangeEvent(this ));
0736: }
0737:
0738: /**
0739: * Returns the flag that controls whether zero values in the
0740: * dataset are ignored.
0741: *
0742: * @return A boolean.
0743: *
0744: * @see #setIgnoreZeroValues(boolean)
0745: */
0746: public boolean getIgnoreZeroValues() {
0747: return this .ignoreZeroValues;
0748: }
0749:
0750: /**
0751: * Sets a flag that controls whether zero values are ignored,
0752: * and sends a {@link PlotChangeEvent} to all registered listeners. This
0753: * only affects whether or not a label appears for the non-visible
0754: * pie section.
0755: *
0756: * @param flag the flag.
0757: *
0758: * @see #getIgnoreZeroValues()
0759: * @see #setIgnoreNullValues(boolean)
0760: */
0761: public void setIgnoreZeroValues(boolean flag) {
0762: this .ignoreZeroValues = flag;
0763: notifyListeners(new PlotChangeEvent(this ));
0764: }
0765:
0766: //// SECTION PAINT ////////////////////////////////////////////////////////
0767:
0768: /**
0769: * Returns the paint for the specified section. This is equivalent to
0770: * <code>lookupSectionPaint(section, false)</code>.
0771: *
0772: * @param key the section key.
0773: *
0774: * @return The paint for the specified section.
0775: *
0776: * @since 1.0.3
0777: *
0778: * @see #lookupSectionPaint(Comparable, boolean)
0779: */
0780: protected Paint lookupSectionPaint(Comparable key) {
0781: return lookupSectionPaint(key, false);
0782: }
0783:
0784: /**
0785: * Returns the paint for the specified section. The lookup involves these
0786: * steps:
0787: * <ul>
0788: * <li>if {@link #getSectionPaint()} is non-<code>null</code>, return
0789: * it;</li>
0790: * <li>if {@link #getSectionPaint(int)} is non-<code>null</code> return
0791: * it;</li>
0792: * <li>if {@link #getSectionPaint(int)} is <code>null</code> but
0793: * <code>autoPopulate</code> is <code>true</code>, attempt to fetch
0794: * a new paint from the drawing supplier
0795: * ({@link #getDrawingSupplier()});
0796: * <li>if all else fails, return {@link #getBaseSectionPaint()}.
0797: * </ul>
0798: *
0799: * @param key the section key.
0800: * @param autoPopulate a flag that controls whether the drawing supplier
0801: * is used to auto-populate the section paint settings.
0802: *
0803: * @return The paint.
0804: *
0805: * @since 1.0.3
0806: */
0807: protected Paint lookupSectionPaint(Comparable key,
0808: boolean autoPopulate) {
0809:
0810: // is there an override?
0811: Paint result = getSectionPaint();
0812: if (result != null) {
0813: return result;
0814: }
0815:
0816: // if not, check if there is a paint defined for the specified key
0817: result = this .sectionPaintMap.getPaint(key);
0818: if (result != null) {
0819: return result;
0820: }
0821:
0822: // nothing defined - do we autoPopulate?
0823: if (autoPopulate) {
0824: DrawingSupplier ds = getDrawingSupplier();
0825: if (ds != null) {
0826: result = ds.getNextPaint();
0827: this .sectionPaintMap.put(key, result);
0828: } else {
0829: result = this .baseSectionPaint;
0830: }
0831: } else {
0832: result = this .baseSectionPaint;
0833: }
0834: return result;
0835: }
0836:
0837: /**
0838: * Returns the paint for ALL sections in the plot.
0839: *
0840: * @return The paint (possibly <code>null</code>).
0841: *
0842: * @see #setSectionPaint(Paint)
0843: *
0844: * @deprecated Use {@link #getSectionPaint(Comparable)} and
0845: * {@link #getBaseSectionPaint()}. Deprecated as of version 1.0.6.
0846: */
0847: public Paint getSectionPaint() {
0848: return this .sectionPaint;
0849: }
0850:
0851: /**
0852: * Sets the paint for ALL sections in the plot. If this is set to
0853: * </code>null</code>, then a list of paints is used instead (to allow
0854: * different colors to be used for each section).
0855: *
0856: * @param paint the paint (<code>null</code> permitted).
0857: *
0858: * @see #getSectionPaint()
0859: *
0860: * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} and
0861: * {@link #setBaseSectionPaint(Paint)}. Deprecated as of version 1.0.6.
0862: */
0863: public void setSectionPaint(Paint paint) {
0864: this .sectionPaint = paint;
0865: notifyListeners(new PlotChangeEvent(this ));
0866: }
0867:
0868: /**
0869: * Returns a key for the specified section. If there is no such section
0870: * in the dataset, we generate a key. This is to provide some backward
0871: * compatibility for the (now deprecated) methods that get/set attributes
0872: * based on section indices. The preferred way of doing this now is to
0873: * link the attributes directly to the section key (there are new methods
0874: * for this, starting from version 1.0.3).
0875: *
0876: * @param section the section index.
0877: *
0878: * @return The key.
0879: *
0880: * @since 1.0.3
0881: */
0882: protected Comparable getSectionKey(int section) {
0883: Comparable key = null;
0884: if (this .dataset != null) {
0885: if (section >= 0 && section < this .dataset.getItemCount()) {
0886: key = this .dataset.getKey(section);
0887: }
0888: }
0889: if (key == null) {
0890: key = new Integer(section);
0891: }
0892: return key;
0893: }
0894:
0895: /**
0896: * Returns the paint associated with the specified key, or
0897: * <code>null</code> if there is no paint associated with the key.
0898: *
0899: * @param key the key (<code>null</code> not permitted).
0900: *
0901: * @return The paint associated with the specified key, or
0902: * <code>null</code>.
0903: *
0904: * @throws IllegalArgumentException if <code>key</code> is
0905: * <code>null</code>.
0906: *
0907: * @see #setSectionPaint(Comparable, Paint)
0908: *
0909: * @since 1.0.3
0910: */
0911: public Paint getSectionPaint(Comparable key) {
0912: // null argument check delegated...
0913: return this .sectionPaintMap.getPaint(key);
0914: }
0915:
0916: /**
0917: * Sets the paint associated with the specified key, and sends a
0918: * {@link PlotChangeEvent} to all registered listeners.
0919: *
0920: * @param key the key (<code>null</code> not permitted).
0921: * @param paint the paint.
0922: *
0923: * @throws IllegalArgumentException if <code>key</code> is
0924: * <code>null</code>.
0925: *
0926: * @see #getSectionPaint(Comparable)
0927: *
0928: * @since 1.0.3
0929: */
0930: public void setSectionPaint(Comparable key, Paint paint) {
0931: // null argument check delegated...
0932: this .sectionPaintMap.put(key, paint);
0933: notifyListeners(new PlotChangeEvent(this ));
0934: }
0935:
0936: /**
0937: * Returns the base section paint. This is used when no other paint is
0938: * defined, which is rare. The default value is <code>Color.gray</code>.
0939: *
0940: * @return The paint (never <code>null</code>).
0941: *
0942: * @see #setBaseSectionPaint(Paint)
0943: */
0944: public Paint getBaseSectionPaint() {
0945: return this .baseSectionPaint;
0946: }
0947:
0948: /**
0949: * Sets the base section paint and sends a {@link PlotChangeEvent} to all
0950: * registered listeners.
0951: *
0952: * @param paint the paint (<code>null</code> not permitted).
0953: *
0954: * @see #getBaseSectionPaint()
0955: */
0956: public void setBaseSectionPaint(Paint paint) {
0957: if (paint == null) {
0958: throw new IllegalArgumentException("Null 'paint' argument.");
0959: }
0960: this .baseSectionPaint = paint;
0961: notifyListeners(new PlotChangeEvent(this ));
0962: }
0963:
0964: //// SECTION OUTLINE PAINT ////////////////////////////////////////////////
0965:
0966: /**
0967: * Returns the flag that controls whether or not the outline is drawn for
0968: * each pie section.
0969: *
0970: * @return The flag that controls whether or not the outline is drawn for
0971: * each pie section.
0972: *
0973: * @see #setSectionOutlinesVisible(boolean)
0974: */
0975: public boolean getSectionOutlinesVisible() {
0976: return this .sectionOutlinesVisible;
0977: }
0978:
0979: /**
0980: * Sets the flag that controls whether or not the outline is drawn for
0981: * each pie section, and sends a {@link PlotChangeEvent} to all registered
0982: * listeners.
0983: *
0984: * @param visible the flag.
0985: *
0986: * @see #getSectionOutlinesVisible()
0987: */
0988: public void setSectionOutlinesVisible(boolean visible) {
0989: this .sectionOutlinesVisible = visible;
0990: notifyListeners(new PlotChangeEvent(this ));
0991: }
0992:
0993: /**
0994: * Returns the outline paint for the specified section. This is equivalent
0995: * to <code>lookupSectionPaint(section, false)</code>.
0996: *
0997: * @param key the section key.
0998: *
0999: * @return The paint for the specified section.
1000: *
1001: * @since 1.0.3
1002: *
1003: * @see #lookupSectionOutlinePaint(Comparable, boolean)
1004: */
1005: protected Paint lookupSectionOutlinePaint(Comparable key) {
1006: return lookupSectionOutlinePaint(key, false);
1007: }
1008:
1009: /**
1010: * Returns the outline paint for the specified section. The lookup
1011: * involves these steps:
1012: * <ul>
1013: * <li>if {@link #getSectionOutlinePaint()} is non-<code>null</code>,
1014: * return it;</li>
1015: * <li>otherwise, if {@link #getSectionOutlinePaint(int)} is
1016: * non-<code>null</code> return it;</li>
1017: * <li>if {@link #getSectionOutlinePaint(int)} is <code>null</code> but
1018: * <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1019: * a new outline paint from the drawing supplier
1020: * ({@link #getDrawingSupplier()});
1021: * <li>if all else fails, return {@link #getBaseSectionOutlinePaint()}.
1022: * </ul>
1023: *
1024: * @param key the section key.
1025: * @param autoPopulate a flag that controls whether the drawing supplier
1026: * is used to auto-populate the section outline paint settings.
1027: *
1028: * @return The paint.
1029: *
1030: * @since 1.0.3
1031: */
1032: protected Paint lookupSectionOutlinePaint(Comparable key,
1033: boolean autoPopulate) {
1034:
1035: // is there an override?
1036: Paint result = getSectionOutlinePaint();
1037: if (result != null) {
1038: return result;
1039: }
1040:
1041: // if not, check if there is a paint defined for the specified key
1042: result = this .sectionOutlinePaintMap.getPaint(key);
1043: if (result != null) {
1044: return result;
1045: }
1046:
1047: // nothing defined - do we autoPopulate?
1048: if (autoPopulate) {
1049: DrawingSupplier ds = getDrawingSupplier();
1050: if (ds != null) {
1051: result = ds.getNextOutlinePaint();
1052: this .sectionOutlinePaintMap.put(key, result);
1053: } else {
1054: result = this .baseSectionOutlinePaint;
1055: }
1056: } else {
1057: result = this .baseSectionOutlinePaint;
1058: }
1059: return result;
1060: }
1061:
1062: /**
1063: * Returns the outline paint for ALL sections in the plot.
1064: *
1065: * @return The paint (possibly <code>null</code>).
1066: *
1067: * @see #setSectionOutlinePaint(Paint)
1068: *
1069: * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} and
1070: * {@link #getBaseSectionOutlinePaint()}. Deprecated as of version
1071: * 1.0.6.
1072: */
1073: public Paint getSectionOutlinePaint() {
1074: return this .sectionOutlinePaint;
1075: }
1076:
1077: /**
1078: * Sets the outline paint for ALL sections in the plot. If this is set to
1079: * </code>null</code>, then a list of paints is used instead (to allow
1080: * different colors to be used for each section).
1081: *
1082: * @param paint the paint (<code>null</code> permitted).
1083: *
1084: * @see #getSectionOutlinePaint()
1085: *
1086: * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)} and
1087: * {@link #setBaseSectionOutlinePaint(Paint)}. Deprecated as of
1088: * version 1.0.6.
1089: */
1090: public void setSectionOutlinePaint(Paint paint) {
1091: this .sectionOutlinePaint = paint;
1092: notifyListeners(new PlotChangeEvent(this ));
1093: }
1094:
1095: /**
1096: * Returns the outline paint associated with the specified key, or
1097: * <code>null</code> if there is no paint associated with the key.
1098: *
1099: * @param key the key (<code>null</code> not permitted).
1100: *
1101: * @return The paint associated with the specified key, or
1102: * <code>null</code>.
1103: *
1104: * @throws IllegalArgumentException if <code>key</code> is
1105: * <code>null</code>.
1106: *
1107: * @see #setSectionOutlinePaint(Comparable, Paint)
1108: *
1109: * @since 1.0.3
1110: */
1111: public Paint getSectionOutlinePaint(Comparable key) {
1112: // null argument check delegated...
1113: return this .sectionOutlinePaintMap.getPaint(key);
1114: }
1115:
1116: /**
1117: * Sets the outline paint associated with the specified key, and sends a
1118: * {@link PlotChangeEvent} to all registered listeners.
1119: *
1120: * @param key the key (<code>null</code> not permitted).
1121: * @param paint the paint.
1122: *
1123: * @throws IllegalArgumentException if <code>key</code> is
1124: * <code>null</code>.
1125: *
1126: * @see #getSectionOutlinePaint(Comparable)
1127: *
1128: * @since 1.0.3
1129: */
1130: public void setSectionOutlinePaint(Comparable key, Paint paint) {
1131: // null argument check delegated...
1132: this .sectionOutlinePaintMap.put(key, paint);
1133: notifyListeners(new PlotChangeEvent(this ));
1134: }
1135:
1136: /**
1137: * Returns the base section paint. This is used when no other paint is
1138: * available.
1139: *
1140: * @return The paint (never <code>null</code>).
1141: *
1142: * @see #setBaseSectionOutlinePaint(Paint)
1143: */
1144: public Paint getBaseSectionOutlinePaint() {
1145: return this .baseSectionOutlinePaint;
1146: }
1147:
1148: /**
1149: * Sets the base section paint.
1150: *
1151: * @param paint the paint (<code>null</code> not permitted).
1152: *
1153: * @see #getBaseSectionOutlinePaint()
1154: */
1155: public void setBaseSectionOutlinePaint(Paint paint) {
1156: if (paint == null) {
1157: throw new IllegalArgumentException("Null 'paint' argument.");
1158: }
1159: this .baseSectionOutlinePaint = paint;
1160: notifyListeners(new PlotChangeEvent(this ));
1161: }
1162:
1163: //// SECTION OUTLINE STROKE ///////////////////////////////////////////////
1164:
1165: /**
1166: * Returns the outline stroke for the specified section. This is equivalent
1167: * to <code>lookupSectionOutlineStroke(section, false)</code>.
1168: *
1169: * @param key the section key.
1170: *
1171: * @return The stroke for the specified section.
1172: *
1173: * @since 1.0.3
1174: *
1175: * @see #lookupSectionOutlineStroke(Comparable, boolean)
1176: */
1177: protected Stroke lookupSectionOutlineStroke(Comparable key) {
1178: return lookupSectionOutlineStroke(key, false);
1179: }
1180:
1181: /**
1182: * Returns the outline stroke for the specified section. The lookup
1183: * involves these steps:
1184: * <ul>
1185: * <li>if {@link #getSectionOutlineStroke()} is non-<code>null</code>,
1186: * return it;</li>
1187: * <li>otherwise, if {@link #getSectionOutlineStroke(int)} is
1188: * non-<code>null</code> return it;</li>
1189: * <li>if {@link #getSectionOutlineStroke(int)} is <code>null</code> but
1190: * <code>autoPopulate</code> is <code>true</code>, attempt to fetch
1191: * a new outline stroke from the drawing supplier
1192: * ({@link #getDrawingSupplier()});
1193: * <li>if all else fails, return {@link #getBaseSectionOutlineStroke()}.
1194: * </ul>
1195: *
1196: * @param key the section key.
1197: * @param autoPopulate a flag that controls whether the drawing supplier
1198: * is used to auto-populate the section outline stroke settings.
1199: *
1200: * @return The stroke.
1201: *
1202: * @since 1.0.3
1203: */
1204: protected Stroke lookupSectionOutlineStroke(Comparable key,
1205: boolean autoPopulate) {
1206:
1207: // is there an override?
1208: Stroke result = getSectionOutlineStroke();
1209: if (result != null) {
1210: return result;
1211: }
1212:
1213: // if not, check if there is a stroke defined for the specified key
1214: result = this .sectionOutlineStrokeMap.getStroke(key);
1215: if (result != null) {
1216: return result;
1217: }
1218:
1219: // nothing defined - do we autoPopulate?
1220: if (autoPopulate) {
1221: DrawingSupplier ds = getDrawingSupplier();
1222: if (ds != null) {
1223: result = ds.getNextOutlineStroke();
1224: this .sectionOutlineStrokeMap.put(key, result);
1225: } else {
1226: result = this .baseSectionOutlineStroke;
1227: }
1228: } else {
1229: result = this .baseSectionOutlineStroke;
1230: }
1231: return result;
1232: }
1233:
1234: /**
1235: * Returns the outline stroke for ALL sections in the plot.
1236: *
1237: * @return The stroke (possibly <code>null</code>).
1238: *
1239: * @see #setSectionOutlineStroke(Stroke)
1240: *
1241: * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} and
1242: * {@link #getBaseSectionOutlineStroke()}. Deprecated as of version
1243: * 1.0.6.
1244: */
1245: public Stroke getSectionOutlineStroke() {
1246: return this .sectionOutlineStroke;
1247: }
1248:
1249: /**
1250: * Sets the outline stroke for ALL sections in the plot. If this is set to
1251: * </code>null</code>, then a list of paints is used instead (to allow
1252: * different colors to be used for each section).
1253: *
1254: * @param stroke the stroke (<code>null</code> permitted).
1255: *
1256: * @see #getSectionOutlineStroke()
1257: *
1258: * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} and
1259: * {@link #setBaseSectionOutlineStroke(Stroke)}. Deprecated as of
1260: * version 1.0.6.
1261: */
1262: public void setSectionOutlineStroke(Stroke stroke) {
1263: this .sectionOutlineStroke = stroke;
1264: notifyListeners(new PlotChangeEvent(this ));
1265: }
1266:
1267: /**
1268: * Returns the outline stroke associated with the specified key, or
1269: * <code>null</code> if there is no stroke associated with the key.
1270: *
1271: * @param key the key (<code>null</code> not permitted).
1272: *
1273: * @return The stroke associated with the specified key, or
1274: * <code>null</code>.
1275: *
1276: * @throws IllegalArgumentException if <code>key</code> is
1277: * <code>null</code>.
1278: *
1279: * @see #setSectionOutlineStroke(Comparable, Stroke)
1280: *
1281: * @since 1.0.3
1282: */
1283: public Stroke getSectionOutlineStroke(Comparable key) {
1284: // null argument check delegated...
1285: return this .sectionOutlineStrokeMap.getStroke(key);
1286: }
1287:
1288: /**
1289: * Sets the outline stroke associated with the specified key, and sends a
1290: * {@link PlotChangeEvent} to all registered listeners.
1291: *
1292: * @param key the key (<code>null</code> not permitted).
1293: * @param stroke the stroke.
1294: *
1295: * @throws IllegalArgumentException if <code>key</code> is
1296: * <code>null</code>.
1297: *
1298: * @see #getSectionOutlineStroke(Comparable)
1299: *
1300: * @since 1.0.3
1301: */
1302: public void setSectionOutlineStroke(Comparable key, Stroke stroke) {
1303: // null argument check delegated...
1304: this .sectionOutlineStrokeMap.put(key, stroke);
1305: notifyListeners(new PlotChangeEvent(this ));
1306: }
1307:
1308: /**
1309: * Returns the base section stroke. This is used when no other stroke is
1310: * available.
1311: *
1312: * @return The stroke (never <code>null</code>).
1313: *
1314: * @see #setBaseSectionOutlineStroke(Stroke)
1315: */
1316: public Stroke getBaseSectionOutlineStroke() {
1317: return this .baseSectionOutlineStroke;
1318: }
1319:
1320: /**
1321: * Sets the base section stroke.
1322: *
1323: * @param stroke the stroke (<code>null</code> not permitted).
1324: *
1325: * @see #getBaseSectionOutlineStroke()
1326: */
1327: public void setBaseSectionOutlineStroke(Stroke stroke) {
1328: if (stroke == null) {
1329: throw new IllegalArgumentException(
1330: "Null 'stroke' argument.");
1331: }
1332: this .baseSectionOutlineStroke = stroke;
1333: notifyListeners(new PlotChangeEvent(this ));
1334: }
1335:
1336: /**
1337: * Returns the shadow paint.
1338: *
1339: * @return The paint (possibly <code>null</code>).
1340: *
1341: * @see #setShadowPaint(Paint)
1342: */
1343: public Paint getShadowPaint() {
1344: return this .shadowPaint;
1345: }
1346:
1347: /**
1348: * Sets the shadow paint and sends a {@link PlotChangeEvent} to all
1349: * registered listeners.
1350: *
1351: * @param paint the paint (<code>null</code> permitted).
1352: *
1353: * @see #getShadowPaint()
1354: */
1355: public void setShadowPaint(Paint paint) {
1356: this .shadowPaint = paint;
1357: notifyListeners(new PlotChangeEvent(this ));
1358: }
1359:
1360: /**
1361: * Returns the x-offset for the shadow effect.
1362: *
1363: * @return The offset (in Java2D units).
1364: *
1365: * @see #setShadowXOffset(double)
1366: */
1367: public double getShadowXOffset() {
1368: return this .shadowXOffset;
1369: }
1370:
1371: /**
1372: * Sets the x-offset for the shadow effect and sends a
1373: * {@link PlotChangeEvent} to all registered listeners.
1374: *
1375: * @param offset the offset (in Java2D units).
1376: *
1377: * @see #getShadowXOffset()
1378: */
1379: public void setShadowXOffset(double offset) {
1380: this .shadowXOffset = offset;
1381: notifyListeners(new PlotChangeEvent(this ));
1382: }
1383:
1384: /**
1385: * Returns the y-offset for the shadow effect.
1386: *
1387: * @return The offset (in Java2D units).
1388: *
1389: * @see #setShadowYOffset(double)
1390: */
1391: public double getShadowYOffset() {
1392: return this .shadowYOffset;
1393: }
1394:
1395: /**
1396: * Sets the y-offset for the shadow effect and sends a
1397: * {@link PlotChangeEvent} to all registered listeners.
1398: *
1399: * @param offset the offset (in Java2D units).
1400: *
1401: * @see #getShadowYOffset()
1402: */
1403: public void setShadowYOffset(double offset) {
1404: this .shadowYOffset = offset;
1405: notifyListeners(new PlotChangeEvent(this ));
1406: }
1407:
1408: /**
1409: * Returns the amount that the section with the specified key should be
1410: * exploded.
1411: *
1412: * @param key the key (<code>null</code> not permitted).
1413: *
1414: * @return The amount that the section with the specified key should be
1415: * exploded.
1416: *
1417: * @throws IllegalArgumentException if <code>key</code> is
1418: * <code>null</code>.
1419: *
1420: * @since 1.0.3
1421: *
1422: * @see #setExplodePercent(Comparable, double)
1423: */
1424: public double getExplodePercent(Comparable key) {
1425: double result = 0.0;
1426: if (this .explodePercentages != null) {
1427: Number percent = (Number) this .explodePercentages.get(key);
1428: if (percent != null) {
1429: result = percent.doubleValue();
1430: }
1431: }
1432: return result;
1433: }
1434:
1435: /**
1436: * Sets the amount that a pie section should be exploded and sends a
1437: * {@link PlotChangeEvent} to all registered listeners.
1438: *
1439: * @param key the section key (<code>null</code> not permitted).
1440: * @param percent the explode percentage (0.30 = 30 percent).
1441: *
1442: * @since 1.0.3
1443: *
1444: * @see #getExplodePercent(Comparable)
1445: */
1446: public void setExplodePercent(Comparable key, double percent) {
1447: if (key == null) {
1448: throw new IllegalArgumentException("Null 'key' argument.");
1449: }
1450: if (this .explodePercentages == null) {
1451: this .explodePercentages = new TreeMap();
1452: }
1453: this .explodePercentages.put(key, new Double(percent));
1454: notifyListeners(new PlotChangeEvent(this ));
1455: }
1456:
1457: /**
1458: * Returns the maximum explode percent.
1459: *
1460: * @return The percent.
1461: */
1462: public double getMaximumExplodePercent() {
1463: double result = 0.0;
1464: Iterator iterator = this .dataset.getKeys().iterator();
1465: while (iterator.hasNext()) {
1466: Comparable key = (Comparable) iterator.next();
1467: Number explode = (Number) this .explodePercentages.get(key);
1468: if (explode != null) {
1469: result = Math.max(result, explode.doubleValue());
1470: }
1471: }
1472: return result;
1473: }
1474:
1475: /**
1476: * Returns the section label generator.
1477: *
1478: * @return The generator (possibly <code>null</code>).
1479: *
1480: * @see #setLabelGenerator(PieSectionLabelGenerator)
1481: */
1482: public PieSectionLabelGenerator getLabelGenerator() {
1483: return this .labelGenerator;
1484: }
1485:
1486: /**
1487: * Sets the section label generator and sends a {@link PlotChangeEvent} to
1488: * all registered listeners.
1489: *
1490: * @param generator the generator (<code>null</code> permitted).
1491: *
1492: * @see #getLabelGenerator()
1493: */
1494: public void setLabelGenerator(PieSectionLabelGenerator generator) {
1495: this .labelGenerator = generator;
1496: notifyListeners(new PlotChangeEvent(this ));
1497: }
1498:
1499: /**
1500: * Returns the gap between the edge of the pie and the labels, expressed as
1501: * a percentage of the plot width.
1502: *
1503: * @return The gap (a percentage, where 0.05 = five percent).
1504: *
1505: * @see #setLabelGap(double)
1506: */
1507: public double getLabelGap() {
1508: return this .labelGap;
1509: }
1510:
1511: /**
1512: * Sets the gap between the edge of the pie and the labels (expressed as a
1513: * percentage of the plot width) and sends a {@link PlotChangeEvent} to all
1514: * registered listeners.
1515: *
1516: * @param gap the gap (a percentage, where 0.05 = five percent).
1517: *
1518: * @see #getLabelGap()
1519: */
1520: public void setLabelGap(double gap) {
1521: this .labelGap = gap;
1522: notifyListeners(new PlotChangeEvent(this ));
1523: }
1524:
1525: /**
1526: * Returns the maximum label width as a percentage of the plot width.
1527: *
1528: * @return The width (a percentage, where 0.20 = 20 percent).
1529: *
1530: * @see #setMaximumLabelWidth(double)
1531: */
1532: public double getMaximumLabelWidth() {
1533: return this .maximumLabelWidth;
1534: }
1535:
1536: /**
1537: * Sets the maximum label width as a percentage of the plot width and sends
1538: * a {@link PlotChangeEvent} to all registered listeners.
1539: *
1540: * @param width the width (a percentage, where 0.20 = 20 percent).
1541: *
1542: * @see #getMaximumLabelWidth()
1543: */
1544: public void setMaximumLabelWidth(double width) {
1545: this .maximumLabelWidth = width;
1546: notifyListeners(new PlotChangeEvent(this ));
1547: }
1548:
1549: /**
1550: * Returns the flag that controls whether or not label linking lines are
1551: * visible.
1552: *
1553: * @return A boolean.
1554: *
1555: * @see #setLabelLinksVisible(boolean)
1556: */
1557: public boolean getLabelLinksVisible() {
1558: return this .labelLinksVisible;
1559: }
1560:
1561: /**
1562: * Sets the flag that controls whether or not label linking lines are
1563: * visible and sends a {@link PlotChangeEvent} to all registered listeners.
1564: * Please take care when hiding the linking lines - depending on the data
1565: * values, the labels can be displayed some distance away from the
1566: * corresponding pie section.
1567: *
1568: * @param visible the flag.
1569: *
1570: * @see #getLabelLinksVisible()
1571: */
1572: public void setLabelLinksVisible(boolean visible) {
1573: this .labelLinksVisible = visible;
1574: notifyListeners(new PlotChangeEvent(this ));
1575: }
1576:
1577: /**
1578: * Returns the margin (expressed as a percentage of the width or height)
1579: * between the edge of the pie and the link point.
1580: *
1581: * @return The link margin (as a percentage, where 0.05 is five percent).
1582: *
1583: * @see #setLabelLinkMargin(double)
1584: */
1585: public double getLabelLinkMargin() {
1586: return this .labelLinkMargin;
1587: }
1588:
1589: /**
1590: * Sets the link margin and sends a {@link PlotChangeEvent} to all
1591: * registered listeners.
1592: *
1593: * @param margin the margin.
1594: *
1595: * @see #getLabelLinkMargin()
1596: */
1597: public void setLabelLinkMargin(double margin) {
1598: this .labelLinkMargin = margin;
1599: notifyListeners(new PlotChangeEvent(this ));
1600: }
1601:
1602: /**
1603: * Returns the paint used for the lines that connect pie sections to their
1604: * corresponding labels.
1605: *
1606: * @return The paint (never <code>null</code>).
1607: *
1608: * @see #setLabelLinkPaint(Paint)
1609: */
1610: public Paint getLabelLinkPaint() {
1611: return this .labelLinkPaint;
1612: }
1613:
1614: /**
1615: * Sets the paint used for the lines that connect pie sections to their
1616: * corresponding labels, and sends a {@link PlotChangeEvent} to all
1617: * registered listeners.
1618: *
1619: * @param paint the paint (<code>null</code> not permitted).
1620: *
1621: * @see #getLabelLinkPaint()
1622: */
1623: public void setLabelLinkPaint(Paint paint) {
1624: if (paint == null) {
1625: throw new IllegalArgumentException("Null 'paint' argument.");
1626: }
1627: this .labelLinkPaint = paint;
1628: notifyListeners(new PlotChangeEvent(this ));
1629: }
1630:
1631: /**
1632: * Returns the stroke used for the label linking lines.
1633: *
1634: * @return The stroke.
1635: *
1636: * @see #setLabelLinkStroke(Stroke)
1637: */
1638: public Stroke getLabelLinkStroke() {
1639: return this .labelLinkStroke;
1640: }
1641:
1642: /**
1643: * Sets the link stroke and sends a {@link PlotChangeEvent} to all
1644: * registered listeners.
1645: *
1646: * @param stroke the stroke.
1647: *
1648: * @see #getLabelLinkStroke()
1649: */
1650: public void setLabelLinkStroke(Stroke stroke) {
1651: if (stroke == null) {
1652: throw new IllegalArgumentException(
1653: "Null 'stroke' argument.");
1654: }
1655: this .labelLinkStroke = stroke;
1656: notifyListeners(new PlotChangeEvent(this ));
1657: }
1658:
1659: /**
1660: * Returns the section label font.
1661: *
1662: * @return The font (never <code>null</code>).
1663: *
1664: * @see #setLabelFont(Font)
1665: */
1666: public Font getLabelFont() {
1667: return this .labelFont;
1668: }
1669:
1670: /**
1671: * Sets the section label font and sends a {@link PlotChangeEvent} to all
1672: * registered listeners.
1673: *
1674: * @param font the font (<code>null</code> not permitted).
1675: *
1676: * @see #getLabelFont()
1677: */
1678: public void setLabelFont(Font font) {
1679: if (font == null) {
1680: throw new IllegalArgumentException("Null 'font' argument.");
1681: }
1682: this .labelFont = font;
1683: notifyListeners(new PlotChangeEvent(this ));
1684: }
1685:
1686: /**
1687: * Returns the section label paint.
1688: *
1689: * @return The paint (never <code>null</code>).
1690: *
1691: * @see #setLabelPaint(Paint)
1692: */
1693: public Paint getLabelPaint() {
1694: return this .labelPaint;
1695: }
1696:
1697: /**
1698: * Sets the section label paint and sends a {@link PlotChangeEvent} to all
1699: * registered listeners.
1700: *
1701: * @param paint the paint (<code>null</code> not permitted).
1702: *
1703: * @see #getLabelPaint()
1704: */
1705: public void setLabelPaint(Paint paint) {
1706: if (paint == null) {
1707: throw new IllegalArgumentException("Null 'paint' argument.");
1708: }
1709: this .labelPaint = paint;
1710: notifyListeners(new PlotChangeEvent(this ));
1711: }
1712:
1713: /**
1714: * Returns the section label background paint.
1715: *
1716: * @return The paint (possibly <code>null</code>).
1717: *
1718: * @see #setLabelBackgroundPaint(Paint)
1719: */
1720: public Paint getLabelBackgroundPaint() {
1721: return this .labelBackgroundPaint;
1722: }
1723:
1724: /**
1725: * Sets the section label background paint and sends a
1726: * {@link PlotChangeEvent} to all registered listeners.
1727: *
1728: * @param paint the paint (<code>null</code> permitted).
1729: *
1730: * @see #getLabelBackgroundPaint()
1731: */
1732: public void setLabelBackgroundPaint(Paint paint) {
1733: this .labelBackgroundPaint = paint;
1734: notifyListeners(new PlotChangeEvent(this ));
1735: }
1736:
1737: /**
1738: * Returns the section label outline paint.
1739: *
1740: * @return The paint (possibly <code>null</code>).
1741: *
1742: * @see #setLabelOutlinePaint(Paint)
1743: */
1744: public Paint getLabelOutlinePaint() {
1745: return this .labelOutlinePaint;
1746: }
1747:
1748: /**
1749: * Sets the section label outline paint and sends a
1750: * {@link PlotChangeEvent} to all registered listeners.
1751: *
1752: * @param paint the paint (<code>null</code> permitted).
1753: *
1754: * @see #getLabelOutlinePaint()
1755: */
1756: public void setLabelOutlinePaint(Paint paint) {
1757: this .labelOutlinePaint = paint;
1758: notifyListeners(new PlotChangeEvent(this ));
1759: }
1760:
1761: /**
1762: * Returns the section label outline stroke.
1763: *
1764: * @return The stroke (possibly <code>null</code>).
1765: *
1766: * @see #setLabelOutlineStroke(Stroke)
1767: */
1768: public Stroke getLabelOutlineStroke() {
1769: return this .labelOutlineStroke;
1770: }
1771:
1772: /**
1773: * Sets the section label outline stroke and sends a
1774: * {@link PlotChangeEvent} to all registered listeners.
1775: *
1776: * @param stroke the stroke (<code>null</code> permitted).
1777: *
1778: * @see #getLabelOutlineStroke()
1779: */
1780: public void setLabelOutlineStroke(Stroke stroke) {
1781: this .labelOutlineStroke = stroke;
1782: notifyListeners(new PlotChangeEvent(this ));
1783: }
1784:
1785: /**
1786: * Returns the section label shadow paint.
1787: *
1788: * @return The paint (possibly <code>null</code>).
1789: *
1790: * @see #setLabelShadowPaint(Paint)
1791: */
1792: public Paint getLabelShadowPaint() {
1793: return this .labelShadowPaint;
1794: }
1795:
1796: /**
1797: * Sets the section label shadow paint and sends a {@link PlotChangeEvent}
1798: * to all registered listeners.
1799: *
1800: * @param paint the paint (<code>null</code> permitted).
1801: *
1802: * @see #getLabelShadowPaint()
1803: */
1804: public void setLabelShadowPaint(Paint paint) {
1805: this .labelShadowPaint = paint;
1806: notifyListeners(new PlotChangeEvent(this ));
1807: }
1808:
1809: /**
1810: * Returns the object responsible for the vertical layout of the pie
1811: * section labels.
1812: *
1813: * @return The label distributor (never <code>null</code>).
1814: *
1815: * @since 1.0.6
1816: */
1817: public AbstractPieLabelDistributor getLabelDistributor() {
1818: return this .labelDistributor;
1819: }
1820:
1821: /**
1822: * Sets the label distributor and sends a {@link PlotChangeEvent} to all
1823: * registered listeners.
1824: *
1825: * @param distributor the distributor (<code>null</code> not permitted).
1826: *
1827: * @since 1.0.6
1828: */
1829: public void setLabelDistributor(
1830: AbstractPieLabelDistributor distributor) {
1831: if (distributor == null) {
1832: throw new IllegalArgumentException(
1833: "Null 'distributor' argument.");
1834: }
1835: this .labelDistributor = distributor;
1836: notifyListeners(new PlotChangeEvent(this ));
1837: }
1838:
1839: /**
1840: * Returns the tool tip generator, an object that is responsible for
1841: * generating the text items used for tool tips by the plot. If the
1842: * generator is <code>null</code>, no tool tips will be created.
1843: *
1844: * @return The generator (possibly <code>null</code>).
1845: *
1846: * @see #setToolTipGenerator(PieToolTipGenerator)
1847: */
1848: public PieToolTipGenerator getToolTipGenerator() {
1849: return this .toolTipGenerator;
1850: }
1851:
1852: /**
1853: * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all
1854: * registered listeners. Set the generator to <code>null</code> if you
1855: * don't want any tool tips.
1856: *
1857: * @param generator the generator (<code>null</code> permitted).
1858: *
1859: * @see #getToolTipGenerator()
1860: */
1861: public void setToolTipGenerator(PieToolTipGenerator generator) {
1862: this .toolTipGenerator = generator;
1863: notifyListeners(new PlotChangeEvent(this ));
1864: }
1865:
1866: /**
1867: * Returns the URL generator.
1868: *
1869: * @return The generator (possibly <code>null</code>).
1870: *
1871: * @see #setURLGenerator(PieURLGenerator)
1872: */
1873: public PieURLGenerator getURLGenerator() {
1874: return this .urlGenerator;
1875: }
1876:
1877: /**
1878: * Sets the URL generator and sends a {@link PlotChangeEvent} to all
1879: * registered listeners.
1880: *
1881: * @param generator the generator (<code>null</code> permitted).
1882: *
1883: * @see #getURLGenerator()
1884: */
1885: public void setURLGenerator(PieURLGenerator generator) {
1886: this .urlGenerator = generator;
1887: notifyListeners(new PlotChangeEvent(this ));
1888: }
1889:
1890: /**
1891: * Returns the minimum arc angle that will be drawn. Pie sections for an
1892: * angle smaller than this are not drawn, to avoid a JDK bug.
1893: *
1894: * @return The minimum angle.
1895: *
1896: * @see #setMinimumArcAngleToDraw(double)
1897: */
1898: public double getMinimumArcAngleToDraw() {
1899: return this .minimumArcAngleToDraw;
1900: }
1901:
1902: /**
1903: * Sets the minimum arc angle that will be drawn. Pie sections for an
1904: * angle smaller than this are not drawn, to avoid a JDK bug. See this
1905: * link for details:
1906: * <br><br>
1907: * <a href="http://www.jfree.org/phpBB2/viewtopic.php?t=2707">
1908: * http://www.jfree.org/phpBB2/viewtopic.php?t=2707</a>
1909: * <br><br>
1910: * ...and this bug report in the Java Bug Parade:
1911: * <br><br>
1912: * <a href=
1913: * "http://developer.java.sun.com/developer/bugParade/bugs/4836495.html">
1914: * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html</a>
1915: *
1916: * @param angle the minimum angle.
1917: *
1918: * @see #getMinimumArcAngleToDraw()
1919: */
1920: public void setMinimumArcAngleToDraw(double angle) {
1921: this .minimumArcAngleToDraw = angle;
1922: }
1923:
1924: /**
1925: * Returns the shape used for legend items.
1926: *
1927: * @return The shape (never <code>null</code>).
1928: *
1929: * @see #setLegendItemShape(Shape)
1930: */
1931: public Shape getLegendItemShape() {
1932: return this .legendItemShape;
1933: }
1934:
1935: /**
1936: * Sets the shape used for legend items and sends a {@link PlotChangeEvent}
1937: * to all registered listeners.
1938: *
1939: * @param shape the shape (<code>null</code> not permitted).
1940: *
1941: * @see #getLegendItemShape()
1942: */
1943: public void setLegendItemShape(Shape shape) {
1944: if (shape == null) {
1945: throw new IllegalArgumentException("Null 'shape' argument.");
1946: }
1947: this .legendItemShape = shape;
1948: notifyListeners(new PlotChangeEvent(this ));
1949: }
1950:
1951: /**
1952: * Returns the legend label generator.
1953: *
1954: * @return The legend label generator (never <code>null</code>).
1955: *
1956: * @see #setLegendLabelGenerator(PieSectionLabelGenerator)
1957: */
1958: public PieSectionLabelGenerator getLegendLabelGenerator() {
1959: return this .legendLabelGenerator;
1960: }
1961:
1962: /**
1963: * Sets the legend label generator and sends a {@link PlotChangeEvent} to
1964: * all registered listeners.
1965: *
1966: * @param generator the generator (<code>null</code> not permitted).
1967: *
1968: * @see #getLegendLabelGenerator()
1969: */
1970: public void setLegendLabelGenerator(
1971: PieSectionLabelGenerator generator) {
1972: if (generator == null) {
1973: throw new IllegalArgumentException(
1974: "Null 'generator' argument.");
1975: }
1976: this .legendLabelGenerator = generator;
1977: notifyListeners(new PlotChangeEvent(this ));
1978: }
1979:
1980: /**
1981: * Returns the legend label tool tip generator.
1982: *
1983: * @return The legend label tool tip generator (possibly <code>null</code>).
1984: *
1985: * @see #setLegendLabelToolTipGenerator(PieSectionLabelGenerator)
1986: */
1987: public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
1988: return this .legendLabelToolTipGenerator;
1989: }
1990:
1991: /**
1992: * Sets the legend label tool tip generator and sends a
1993: * {@link PlotChangeEvent} to all registered listeners.
1994: *
1995: * @param generator the generator (<code>null</code> permitted).
1996: *
1997: * @see #getLegendLabelToolTipGenerator()
1998: */
1999: public void setLegendLabelToolTipGenerator(
2000: PieSectionLabelGenerator generator) {
2001: this .legendLabelToolTipGenerator = generator;
2002: notifyListeners(new PlotChangeEvent(this ));
2003: }
2004:
2005: /**
2006: * Returns the legend label URL generator.
2007: *
2008: * @return The legend label URL generator (possibly <code>null</code>).
2009: *
2010: * @see #setLegendLabelURLGenerator(PieURLGenerator)
2011: *
2012: * @since 1.0.4
2013: */
2014: public PieURLGenerator getLegendLabelURLGenerator() {
2015: return this .legendLabelURLGenerator;
2016: }
2017:
2018: /**
2019: * Sets the legend label URL generator and sends a
2020: * {@link PlotChangeEvent} to all registered listeners.
2021: *
2022: * @param generator the generator (<code>null</code> permitted).
2023: *
2024: * @see #getLegendLabelURLGenerator()
2025: *
2026: * @since 1.0.4
2027: */
2028: public void setLegendLabelURLGenerator(PieURLGenerator generator) {
2029: this .legendLabelURLGenerator = generator;
2030: notifyListeners(new PlotChangeEvent(this ));
2031: }
2032:
2033: /**
2034: * Initialises the drawing procedure. This method will be called before
2035: * the first item is rendered, giving the plot an opportunity to initialise
2036: * any state information it wants to maintain.
2037: *
2038: * @param g2 the graphics device.
2039: * @param plotArea the plot area (<code>null</code> not permitted).
2040: * @param plot the plot.
2041: * @param index the secondary index (<code>null</code> for primary
2042: * renderer).
2043: * @param info collects chart rendering information for return to caller.
2044: *
2045: * @return A state object (maintains state information relevant to one
2046: * chart drawing).
2047: */
2048: public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea,
2049: PiePlot plot, Integer index, PlotRenderingInfo info) {
2050:
2051: PiePlotState state = new PiePlotState(info);
2052: state.setPassesRequired(2);
2053: state.setTotal(DatasetUtilities.calculatePieDatasetTotal(plot
2054: .getDataset()));
2055: state.setLatestAngle(plot.getStartAngle());
2056: return state;
2057:
2058: }
2059:
2060: /**
2061: * Draws the plot on a Java 2D graphics device (such as the screen or a
2062: * printer).
2063: *
2064: * @param g2 the graphics device.
2065: * @param area the area within which the plot should be drawn.
2066: * @param anchor the anchor point (<code>null</code> permitted).
2067: * @param parentState the state from the parent plot, if there is one.
2068: * @param info collects info about the drawing
2069: * (<code>null</code> permitted).
2070: */
2071: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2072: PlotState parentState, PlotRenderingInfo info) {
2073:
2074: // adjust for insets...
2075: RectangleInsets insets = getInsets();
2076: insets.trim(area);
2077:
2078: if (info != null) {
2079: info.setPlotArea(area);
2080: info.setDataArea(area);
2081: }
2082:
2083: drawBackground(g2, area);
2084: drawOutline(g2, area);
2085:
2086: Shape savedClip = g2.getClip();
2087: g2.clip(area);
2088:
2089: Composite originalComposite = g2.getComposite();
2090: g2.setComposite(AlphaComposite.getInstance(
2091: AlphaComposite.SRC_OVER, getForegroundAlpha()));
2092:
2093: if (!DatasetUtilities.isEmptyOrNull(this .dataset)) {
2094: drawPie(g2, area, info);
2095: } else {
2096: drawNoDataMessage(g2, area);
2097: }
2098:
2099: g2.setClip(savedClip);
2100: g2.setComposite(originalComposite);
2101:
2102: drawOutline(g2, area);
2103:
2104: }
2105:
2106: /**
2107: * Draws the pie.
2108: *
2109: * @param g2 the graphics device.
2110: * @param plotArea the plot area.
2111: * @param info chart rendering info.
2112: */
2113: protected void drawPie(Graphics2D g2, Rectangle2D plotArea,
2114: PlotRenderingInfo info) {
2115:
2116: PiePlotState state = initialise(g2, plotArea, this , null, info);
2117:
2118: // adjust the plot area for interior spacing and labels...
2119: double labelWidth = 0.0;
2120: if (this .labelGenerator != null) {
2121: labelWidth = this .labelGap + this .maximumLabelWidth
2122: + this .labelLinkMargin;
2123: }
2124: double gapHorizontal = plotArea.getWidth()
2125: * (this .interiorGap + labelWidth);
2126: double gapVertical = plotArea.getHeight() * this .interiorGap;
2127:
2128: double linkX = plotArea.getX() + gapHorizontal / 2;
2129: double linkY = plotArea.getY() + gapVertical / 2;
2130: double linkW = plotArea.getWidth() - gapHorizontal;
2131: double linkH = plotArea.getHeight() - gapVertical;
2132:
2133: // make the link area a square if the pie chart is to be circular...
2134: if (this .circular) {
2135: double min = Math.min(linkW, linkH) / 2;
2136: linkX = (linkX + linkX + linkW) / 2 - min;
2137: linkY = (linkY + linkY + linkH) / 2 - min;
2138: linkW = 2 * min;
2139: linkH = 2 * min;
2140: }
2141:
2142: // the link area defines the dog leg points for the linking lines to
2143: // the labels
2144: Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY,
2145: linkW, linkH);
2146: state.setLinkArea(linkArea);
2147:
2148: // the explode area defines the max circle/ellipse for the exploded
2149: // pie sections. it is defined by shrinking the linkArea by the
2150: // linkMargin factor.
2151: double hh = linkArea.getWidth() * this .labelLinkMargin;
2152: double vv = linkArea.getHeight() * this .labelLinkMargin;
2153: Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh
2154: / 2.0, linkY + vv / 2.0, linkW - hh, linkH - vv);
2155:
2156: state.setExplodedPieArea(explodeArea);
2157:
2158: // the pie area defines the circle/ellipse for regular pie sections.
2159: // it is defined by shrinking the explodeArea by the explodeMargin
2160: // factor.
2161: double maximumExplodePercent = getMaximumExplodePercent();
2162: double percent = maximumExplodePercent
2163: / (1.0 + maximumExplodePercent);
2164:
2165: double h1 = explodeArea.getWidth() * percent;
2166: double v1 = explodeArea.getHeight() * percent;
2167: Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX()
2168: + h1 / 2.0, explodeArea.getY() + v1 / 2.0, explodeArea
2169: .getWidth()
2170: - h1, explodeArea.getHeight() - v1);
2171:
2172: state.setPieArea(pieArea);
2173: state.setPieCenterX(pieArea.getCenterX());
2174: state.setPieCenterY(pieArea.getCenterY());
2175: state.setPieWRadius(pieArea.getWidth() / 2.0);
2176: state.setPieHRadius(pieArea.getHeight() / 2.0);
2177: // plot the data (unless the dataset is null)...
2178: if ((this .dataset != null)
2179: && (this .dataset.getKeys().size() > 0)) {
2180:
2181: List keys = this .dataset.getKeys();
2182: double totalValue = DatasetUtilities
2183: .calculatePieDatasetTotal(this .dataset);
2184:
2185: int passesRequired = state.getPassesRequired();
2186: for (int pass = 0; pass < passesRequired; pass++) {
2187: double runningTotal = 0.0;
2188: for (int section = 0; section < keys.size(); section++) {
2189: Number n = this .dataset.getValue(section);
2190: if (n != null) {
2191: double value = n.doubleValue();
2192: if (value > 0.0) {
2193: runningTotal += value;
2194: drawItem(g2, section, explodeArea, state,
2195: pass);
2196: }
2197: }
2198: }
2199: }
2200:
2201: drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
2202:
2203: } else {
2204: drawNoDataMessage(g2, plotArea);
2205: }
2206: }
2207:
2208: /**
2209: * Draws a single data item.
2210: *
2211: * @param g2 the graphics device (<code>null</code> not permitted).
2212: * @param section the section index.
2213: * @param dataArea the data plot area.
2214: * @param state state information for one chart.
2215: * @param currentPass the current pass index.
2216: */
2217: protected void drawItem(Graphics2D g2, int section,
2218: Rectangle2D dataArea, PiePlotState state, int currentPass) {
2219:
2220: Number n = this .dataset.getValue(section);
2221: if (n == null) {
2222: return;
2223: }
2224: double value = n.doubleValue();
2225: double angle1 = 0.0;
2226: double angle2 = 0.0;
2227:
2228: if (this .direction == Rotation.CLOCKWISE) {
2229: angle1 = state.getLatestAngle();
2230: angle2 = angle1 - value / state.getTotal() * 360.0;
2231: } else if (this .direction == Rotation.ANTICLOCKWISE) {
2232: angle1 = state.getLatestAngle();
2233: angle2 = angle1 + value / state.getTotal() * 360.0;
2234: } else {
2235: throw new IllegalStateException(
2236: "Rotation type not recognised.");
2237: }
2238:
2239: double angle = (angle2 - angle1);
2240: if (Math.abs(angle) > getMinimumArcAngleToDraw()) {
2241: double ep = 0.0;
2242: double mep = getMaximumExplodePercent();
2243: if (mep > 0.0) {
2244: ep = getExplodePercent(section) / mep;
2245: }
2246: Rectangle2D arcBounds = getArcBounds(state.getPieArea(),
2247: state.getExplodedPieArea(), angle1, angle, ep);
2248: Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1,
2249: angle, Arc2D.PIE);
2250:
2251: if (currentPass == 0) {
2252: if (this .shadowPaint != null) {
2253: Shape shadowArc = ShapeUtilities
2254: .createTranslatedShape(arc,
2255: (float) this .shadowXOffset,
2256: (float) this .shadowYOffset);
2257: g2.setPaint(this .shadowPaint);
2258: g2.fill(shadowArc);
2259: }
2260: } else if (currentPass == 1) {
2261: Comparable key = getSectionKey(section);
2262: Paint paint = lookupSectionPaint(key, true);
2263: g2.setPaint(paint);
2264: g2.fill(arc);
2265:
2266: Paint outlinePaint = lookupSectionOutlinePaint(key);
2267: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2268: if (this .sectionOutlinesVisible) {
2269: g2.setPaint(outlinePaint);
2270: g2.setStroke(outlineStroke);
2271: g2.draw(arc);
2272: }
2273:
2274: // update the linking line target for later
2275: // add an entity for the pie section
2276: if (state.getInfo() != null) {
2277: EntityCollection entities = state
2278: .getEntityCollection();
2279: if (entities != null) {
2280: String tip = null;
2281: if (this .toolTipGenerator != null) {
2282: tip = this .toolTipGenerator
2283: .generateToolTip(this .dataset, key);
2284: }
2285: String url = null;
2286: if (this .urlGenerator != null) {
2287: url = this .urlGenerator.generateURL(
2288: this .dataset, key, this .pieIndex);
2289: }
2290: PieSectionEntity entity = new PieSectionEntity(
2291: arc, this .dataset, this .pieIndex,
2292: section, key, tip, url);
2293: entities.add(entity);
2294: }
2295: }
2296: }
2297: }
2298: state.setLatestAngle(angle2);
2299: }
2300:
2301: /**
2302: * Draws the labels for the pie sections.
2303: *
2304: * @param g2 the graphics device.
2305: * @param keys the keys.
2306: * @param totalValue the total value.
2307: * @param plotArea the plot area.
2308: * @param linkArea the link area.
2309: * @param state the state.
2310: */
2311: protected void drawLabels(Graphics2D g2, List keys,
2312: double totalValue, Rectangle2D plotArea,
2313: Rectangle2D linkArea, PiePlotState state) {
2314:
2315: Composite originalComposite = g2.getComposite();
2316: g2.setComposite(AlphaComposite.getInstance(
2317: AlphaComposite.SRC_OVER, 1.0f));
2318:
2319: // classify the keys according to which side the label will appear...
2320: DefaultKeyedValues leftKeys = new DefaultKeyedValues();
2321: DefaultKeyedValues rightKeys = new DefaultKeyedValues();
2322:
2323: double runningTotal1 = 0.0;
2324: Iterator iterator1 = keys.iterator();
2325: while (iterator1.hasNext()) {
2326: Comparable key = (Comparable) iterator1.next();
2327: boolean include = true;
2328: double v = 0.0;
2329: Number n = this .dataset.getValue(key);
2330: if (n == null) {
2331: include = !this .ignoreNullValues;
2332: } else {
2333: v = n.doubleValue();
2334: include = this .ignoreZeroValues ? v > 0.0 : v >= 0.0;
2335: }
2336:
2337: if (include) {
2338: runningTotal1 = runningTotal1 + v;
2339: // work out the mid angle (0 - 90 and 270 - 360) = right,
2340: // otherwise left
2341: double mid = this .startAngle
2342: + (this .direction.getFactor()
2343: * ((runningTotal1 - v / 2.0) * 360) / totalValue);
2344: if (Math.cos(Math.toRadians(mid)) < 0.0) {
2345: leftKeys.addValue(key, new Double(mid));
2346: } else {
2347: rightKeys.addValue(key, new Double(mid));
2348: }
2349: }
2350: }
2351:
2352: g2.setFont(getLabelFont());
2353: float maxLabelWidth = (float) (getMaximumLabelWidth() * plotArea
2354: .getWidth());
2355:
2356: // draw the labels...
2357: if (this .labelGenerator != null) {
2358: drawLeftLabels(leftKeys, g2, plotArea, linkArea,
2359: maxLabelWidth, state);
2360: drawRightLabels(rightKeys, g2, plotArea, linkArea,
2361: maxLabelWidth, state);
2362: }
2363: g2.setComposite(originalComposite);
2364:
2365: }
2366:
2367: /**
2368: * Draws the left labels.
2369: *
2370: * @param leftKeys a collection of keys and angles (to the middle of the
2371: * section, in degrees) for the sections on the left side of the
2372: * plot.
2373: * @param g2 the graphics device.
2374: * @param plotArea the plot area.
2375: * @param linkArea the link area.
2376: * @param maxLabelWidth the maximum label width.
2377: * @param state the state.
2378: */
2379: protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2,
2380: Rectangle2D plotArea, Rectangle2D linkArea,
2381: float maxLabelWidth, PiePlotState state) {
2382:
2383: this .labelDistributor.clear();
2384: double lGap = plotArea.getWidth() * this .labelGap;
2385: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2386: for (int i = 0; i < leftKeys.getItemCount(); i++) {
2387: String label = this .labelGenerator.generateSectionLabel(
2388: this .dataset, leftKeys.getKey(i));
2389: if (label != null) {
2390: TextBlock block = TextUtilities.createTextBlock(label,
2391: this .labelFont, this .labelPaint, maxLabelWidth,
2392: new G2TextMeasurer(g2));
2393: TextBox labelBox = new TextBox(block);
2394: labelBox.setBackgroundPaint(this .labelBackgroundPaint);
2395: labelBox.setOutlinePaint(this .labelOutlinePaint);
2396: labelBox.setOutlineStroke(this .labelOutlineStroke);
2397: labelBox.setShadowPaint(this .labelShadowPaint);
2398: double theta = Math.toRadians(leftKeys.getValue(i)
2399: .doubleValue());
2400: double baseY = state.getPieCenterY() - Math.sin(theta)
2401: * verticalLinkRadius;
2402: double hh = labelBox.getHeight(g2);
2403:
2404: this .labelDistributor
2405: .addPieLabelRecord(new PieLabelRecord(leftKeys
2406: .getKey(i), theta, baseY, labelBox, hh,
2407: lGap / 2.0 + lGap / 2.0
2408: * -Math.cos(theta),
2409: 0.9 + getExplodePercent(leftKeys
2410: .getKey(i))));
2411: }
2412: }
2413: this .labelDistributor.distributeLabels(plotArea.getMinY(),
2414: plotArea.getHeight());
2415: for (int i = 0; i < this .labelDistributor.getItemCount(); i++) {
2416: drawLeftLabel(g2, state, this .labelDistributor
2417: .getPieLabelRecord(i));
2418: }
2419: }
2420:
2421: /**
2422: * Draws the right labels.
2423: *
2424: * @param keys the keys.
2425: * @param g2 the graphics device.
2426: * @param plotArea the plot area.
2427: * @param linkArea the link area.
2428: * @param maxLabelWidth the maximum label width.
2429: * @param state the state.
2430: */
2431: protected void drawRightLabels(KeyedValues keys, Graphics2D g2,
2432: Rectangle2D plotArea, Rectangle2D linkArea,
2433: float maxLabelWidth, PiePlotState state) {
2434:
2435: // draw the right labels...
2436: this .labelDistributor.clear();
2437: double lGap = plotArea.getWidth() * this .labelGap;
2438: double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0;
2439:
2440: for (int i = 0; i < keys.getItemCount(); i++) {
2441: String label = this .labelGenerator.generateSectionLabel(
2442: this .dataset, keys.getKey(i));
2443:
2444: if (label != null) {
2445: TextBlock block = TextUtilities.createTextBlock(label,
2446: this .labelFont, this .labelPaint, maxLabelWidth,
2447: new G2TextMeasurer(g2));
2448: TextBox labelBox = new TextBox(block);
2449: labelBox.setBackgroundPaint(this .labelBackgroundPaint);
2450: labelBox.setOutlinePaint(this .labelOutlinePaint);
2451: labelBox.setOutlineStroke(this .labelOutlineStroke);
2452: labelBox.setShadowPaint(this .labelShadowPaint);
2453: double theta = Math.toRadians(keys.getValue(i)
2454: .doubleValue());
2455: double baseY = state.getPieCenterY() - Math.sin(theta)
2456: * verticalLinkRadius;
2457: double hh = labelBox.getHeight(g2);
2458: this .labelDistributor
2459: .addPieLabelRecord(new PieLabelRecord(keys
2460: .getKey(i), theta, baseY, labelBox, hh,
2461: lGap / 2.0 + lGap / 2.0
2462: * Math.cos(theta),
2463: 0.9 + getExplodePercent(keys.getKey(i))));
2464: }
2465: }
2466: this .labelDistributor.distributeLabels(plotArea.getMinY(),
2467: plotArea.getHeight());
2468: for (int i = 0; i < this .labelDistributor.getItemCount(); i++) {
2469: drawRightLabel(g2, state, this .labelDistributor
2470: .getPieLabelRecord(i));
2471: }
2472:
2473: }
2474:
2475: /**
2476: * Returns a collection of legend items for the pie chart.
2477: *
2478: * @return The legend items (never <code>null</code>).
2479: */
2480: public LegendItemCollection getLegendItems() {
2481:
2482: LegendItemCollection result = new LegendItemCollection();
2483: if (this .dataset == null) {
2484: return result;
2485: }
2486: List keys = this .dataset.getKeys();
2487: int section = 0;
2488: Shape shape = getLegendItemShape();
2489: Iterator iterator = keys.iterator();
2490: while (iterator.hasNext()) {
2491: Comparable key = (Comparable) iterator.next();
2492: Number n = this .dataset.getValue(key);
2493: boolean include = true;
2494: if (n == null) {
2495: include = !this .ignoreNullValues;
2496: } else {
2497: double v = n.doubleValue();
2498: if (v == 0.0) {
2499: include = !this .ignoreZeroValues;
2500: } else {
2501: include = v > 0.0;
2502: }
2503: }
2504: if (include) {
2505: String label = this .legendLabelGenerator
2506: .generateSectionLabel(this .dataset, key);
2507: if (label != null) {
2508: String description = label;
2509: String toolTipText = null;
2510: if (this .legendLabelToolTipGenerator != null) {
2511: toolTipText = this .legendLabelToolTipGenerator
2512: .generateSectionLabel(this .dataset, key);
2513: }
2514: String urlText = null;
2515: if (this .legendLabelURLGenerator != null) {
2516: urlText = this .legendLabelURLGenerator
2517: .generateURL(this .dataset, key,
2518: this .pieIndex);
2519: }
2520: Paint paint = lookupSectionPaint(key, true);
2521: Paint outlinePaint = lookupSectionOutlinePaint(key);
2522: Stroke outlineStroke = lookupSectionOutlineStroke(key);
2523: LegendItem item = new LegendItem(label,
2524: description, toolTipText, urlText, true,
2525: shape, true, paint, true, outlinePaint,
2526: outlineStroke,
2527: false, // line not visible
2528: new Line2D.Float(), new BasicStroke(),
2529: Color.black);
2530: item.setDataset(getDataset());
2531: result.add(item);
2532: }
2533: section++;
2534: } else {
2535: section++;
2536: }
2537: }
2538: return result;
2539: }
2540:
2541: /**
2542: * Returns a short string describing the type of plot.
2543: *
2544: * @return The plot type.
2545: */
2546: public String getPlotType() {
2547: return localizationResources.getString("Pie_Plot");
2548: }
2549:
2550: /**
2551: * Returns a rectangle that can be used to create a pie section (taking
2552: * into account the amount by which the pie section is 'exploded').
2553: *
2554: * @param unexploded the area inside which the unexploded pie sections are
2555: * drawn.
2556: * @param exploded the area inside which the exploded pie sections are
2557: * drawn.
2558: * @param angle the start angle.
2559: * @param extent the extent of the arc.
2560: * @param explodePercent the amount by which the pie section is exploded.
2561: *
2562: * @return A rectangle that can be used to create a pie section.
2563: */
2564: protected Rectangle2D getArcBounds(Rectangle2D unexploded,
2565: Rectangle2D exploded, double angle, double extent,
2566: double explodePercent) {
2567:
2568: if (explodePercent == 0.0) {
2569: return unexploded;
2570: } else {
2571: Arc2D arc1 = new Arc2D.Double(unexploded, angle,
2572: extent / 2, Arc2D.OPEN);
2573: Point2D point1 = arc1.getEndPoint();
2574: Arc2D.Double arc2 = new Arc2D.Double(exploded, angle,
2575: extent / 2, Arc2D.OPEN);
2576: Point2D point2 = arc2.getEndPoint();
2577: double deltaX = (point1.getX() - point2.getX())
2578: * explodePercent;
2579: double deltaY = (point1.getY() - point2.getY())
2580: * explodePercent;
2581: return new Rectangle2D.Double(unexploded.getX() - deltaX,
2582: unexploded.getY() - deltaY, unexploded.getWidth(),
2583: unexploded.getHeight());
2584: }
2585: }
2586:
2587: /**
2588: * Draws a section label on the left side of the pie chart.
2589: *
2590: * @param g2 the graphics device.
2591: * @param state the state.
2592: * @param record the label record.
2593: */
2594: protected void drawLeftLabel(Graphics2D g2, PiePlotState state,
2595: PieLabelRecord record) {
2596:
2597: double anchorX = state.getLinkArea().getMinX();
2598: double targetX = anchorX - record.getGap();
2599: double targetY = record.getAllocatedY();
2600:
2601: if (this .labelLinksVisible) {
2602: double theta = record.getAngle();
2603: double linkX = state.getPieCenterX() + Math.cos(theta)
2604: * state.getPieWRadius() * record.getLinkPercent();
2605: double linkY = state.getPieCenterY() - Math.sin(theta)
2606: * state.getPieHRadius() * record.getLinkPercent();
2607: double elbowX = state.getPieCenterX() + Math.cos(theta)
2608: * state.getLinkArea().getWidth() / 2.0;
2609: double elbowY = state.getPieCenterY() - Math.sin(theta)
2610: * state.getLinkArea().getHeight() / 2.0;
2611: double anchorY = elbowY;
2612: g2.setPaint(this .labelLinkPaint);
2613: g2.setStroke(this .labelLinkStroke);
2614: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2615: g2
2616: .draw(new Line2D.Double(anchorX, anchorY, elbowX,
2617: elbowY));
2618: g2.draw(new Line2D.Double(anchorX, anchorY, targetX,
2619: targetY));
2620: }
2621: TextBox tb = record.getLabel();
2622: tb.draw(g2, (float) targetX, (float) targetY,
2623: RectangleAnchor.RIGHT);
2624:
2625: }
2626:
2627: /**
2628: * Draws a section label on the right side of the pie chart.
2629: *
2630: * @param g2 the graphics device.
2631: * @param state the state.
2632: * @param record the label record.
2633: */
2634: protected void drawRightLabel(Graphics2D g2, PiePlotState state,
2635: PieLabelRecord record) {
2636:
2637: double anchorX = state.getLinkArea().getMaxX();
2638: double targetX = anchorX + record.getGap();
2639: double targetY = record.getAllocatedY();
2640:
2641: if (this .labelLinksVisible) {
2642: double theta = record.getAngle();
2643: double linkX = state.getPieCenterX() + Math.cos(theta)
2644: * state.getPieWRadius() * record.getLinkPercent();
2645: double linkY = state.getPieCenterY() - Math.sin(theta)
2646: * state.getPieHRadius() * record.getLinkPercent();
2647: double elbowX = state.getPieCenterX() + Math.cos(theta)
2648: * state.getLinkArea().getWidth() / 2.0;
2649: double elbowY = state.getPieCenterY() - Math.sin(theta)
2650: * state.getLinkArea().getHeight() / 2.0;
2651: double anchorY = elbowY;
2652: g2.setPaint(this .labelLinkPaint);
2653: g2.setStroke(this .labelLinkStroke);
2654: g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY));
2655: g2
2656: .draw(new Line2D.Double(anchorX, anchorY, elbowX,
2657: elbowY));
2658: g2.draw(new Line2D.Double(anchorX, anchorY, targetX,
2659: targetY));
2660: }
2661:
2662: TextBox tb = record.getLabel();
2663: tb.draw(g2, (float) targetX, (float) targetY,
2664: RectangleAnchor.LEFT);
2665:
2666: }
2667:
2668: /**
2669: * Tests this plot for equality with an arbitrary object. Note that the
2670: * plot's dataset is NOT included in the test for equality.
2671: *
2672: * @param obj the object to test against (<code>null</code> permitted).
2673: *
2674: * @return <code>true</code> or <code>false</code>.
2675: */
2676: public boolean equals(Object obj) {
2677: if (obj == this ) {
2678: return true;
2679: }
2680: if (!(obj instanceof PiePlot)) {
2681: return false;
2682: }
2683: if (!super .equals(obj)) {
2684: return false;
2685: }
2686: PiePlot that = (PiePlot) obj;
2687: if (this .pieIndex != that.pieIndex) {
2688: return false;
2689: }
2690: if (this .interiorGap != that.interiorGap) {
2691: return false;
2692: }
2693: if (this .circular != that.circular) {
2694: return false;
2695: }
2696: if (this .startAngle != that.startAngle) {
2697: return false;
2698: }
2699: if (this .direction != that.direction) {
2700: return false;
2701: }
2702: if (this .ignoreZeroValues != that.ignoreZeroValues) {
2703: return false;
2704: }
2705: if (this .ignoreNullValues != that.ignoreNullValues) {
2706: return false;
2707: }
2708: if (!PaintUtilities.equal(this .sectionPaint, that.sectionPaint)) {
2709: return false;
2710: }
2711: if (!ObjectUtilities.equal(this .sectionPaintMap,
2712: that.sectionPaintMap)) {
2713: return false;
2714: }
2715: if (!PaintUtilities.equal(this .baseSectionPaint,
2716: that.baseSectionPaint)) {
2717: return false;
2718: }
2719: if (this .sectionOutlinesVisible != that.sectionOutlinesVisible) {
2720: return false;
2721: }
2722: if (!PaintUtilities.equal(this .sectionOutlinePaint,
2723: that.sectionOutlinePaint)) {
2724: return false;
2725: }
2726: if (!ObjectUtilities.equal(this .sectionOutlinePaintMap,
2727: that.sectionOutlinePaintMap)) {
2728: return false;
2729: }
2730: if (!PaintUtilities.equal(this .baseSectionOutlinePaint,
2731: that.baseSectionOutlinePaint)) {
2732: return false;
2733: }
2734: if (!ObjectUtilities.equal(this .sectionOutlineStroke,
2735: that.sectionOutlineStroke)) {
2736: return false;
2737: }
2738: if (!ObjectUtilities.equal(this .sectionOutlineStrokeMap,
2739: that.sectionOutlineStrokeMap)) {
2740: return false;
2741: }
2742: if (!ObjectUtilities.equal(this .baseSectionOutlineStroke,
2743: that.baseSectionOutlineStroke)) {
2744: return false;
2745: }
2746: if (!PaintUtilities.equal(this .shadowPaint, that.shadowPaint)) {
2747: return false;
2748: }
2749: if (!(this .shadowXOffset == that.shadowXOffset)) {
2750: return false;
2751: }
2752: if (!(this .shadowYOffset == that.shadowYOffset)) {
2753: return false;
2754: }
2755: if (!ObjectUtilities.equal(this .explodePercentages,
2756: that.explodePercentages)) {
2757: return false;
2758: }
2759: if (!ObjectUtilities.equal(this .labelGenerator,
2760: that.labelGenerator)) {
2761: return false;
2762: }
2763: if (!ObjectUtilities.equal(this .labelFont, that.labelFont)) {
2764: return false;
2765: }
2766: if (!PaintUtilities.equal(this .labelPaint, that.labelPaint)) {
2767: return false;
2768: }
2769: if (!PaintUtilities.equal(this .labelBackgroundPaint,
2770: that.labelBackgroundPaint)) {
2771: return false;
2772: }
2773: if (!PaintUtilities.equal(this .labelOutlinePaint,
2774: that.labelOutlinePaint)) {
2775: return false;
2776: }
2777: if (!ObjectUtilities.equal(this .labelOutlineStroke,
2778: that.labelOutlineStroke)) {
2779: return false;
2780: }
2781: if (!PaintUtilities.equal(this .labelShadowPaint,
2782: that.labelShadowPaint)) {
2783: return false;
2784: }
2785: if (!(this .maximumLabelWidth == that.maximumLabelWidth)) {
2786: return false;
2787: }
2788: if (!(this .labelGap == that.labelGap)) {
2789: return false;
2790: }
2791: if (!(this .labelLinkMargin == that.labelLinkMargin)) {
2792: return false;
2793: }
2794: if (this .labelLinksVisible != that.labelLinksVisible) {
2795: return false;
2796: }
2797: if (!PaintUtilities.equal(this .labelLinkPaint,
2798: that.labelLinkPaint)) {
2799: return false;
2800: }
2801: if (!ObjectUtilities.equal(this .labelLinkStroke,
2802: that.labelLinkStroke)) {
2803: return false;
2804: }
2805: if (!ObjectUtilities.equal(this .toolTipGenerator,
2806: that.toolTipGenerator)) {
2807: return false;
2808: }
2809: if (!ObjectUtilities
2810: .equal(this .urlGenerator, that.urlGenerator)) {
2811: return false;
2812: }
2813: if (!(this .minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
2814: return false;
2815: }
2816: if (!ShapeUtilities.equal(this .legendItemShape,
2817: that.legendItemShape)) {
2818: return false;
2819: }
2820: if (!ObjectUtilities.equal(this .legendLabelGenerator,
2821: that.legendLabelGenerator)) {
2822: return false;
2823: }
2824: if (!ObjectUtilities.equal(this .legendLabelToolTipGenerator,
2825: that.legendLabelToolTipGenerator)) {
2826: return false;
2827: }
2828: if (!ObjectUtilities.equal(this .legendLabelURLGenerator,
2829: that.legendLabelURLGenerator)) {
2830: return false;
2831: }
2832: // can't find any difference...
2833: return true;
2834: }
2835:
2836: /**
2837: * Returns a clone of the plot.
2838: *
2839: * @return A clone.
2840: *
2841: * @throws CloneNotSupportedException if some component of the plot does
2842: * not support cloning.
2843: */
2844: public Object clone() throws CloneNotSupportedException {
2845: PiePlot clone = (PiePlot) super .clone();
2846: if (clone.dataset != null) {
2847: clone.dataset.addChangeListener(clone);
2848: }
2849: if (this .urlGenerator instanceof PublicCloneable) {
2850: clone.urlGenerator = (PieURLGenerator) ObjectUtilities
2851: .clone(this .urlGenerator);
2852: }
2853: clone.legendItemShape = ShapeUtilities
2854: .clone(this .legendItemShape);
2855: if (this .legendLabelGenerator != null) {
2856: clone.legendLabelGenerator = (PieSectionLabelGenerator) ObjectUtilities
2857: .clone(this .legendLabelGenerator);
2858: }
2859: if (this .legendLabelToolTipGenerator != null) {
2860: clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator) ObjectUtilities
2861: .clone(this .legendLabelToolTipGenerator);
2862: }
2863: if (this .legendLabelURLGenerator instanceof PublicCloneable) {
2864: clone.legendLabelURLGenerator = (PieURLGenerator) ObjectUtilities
2865: .clone(this .legendLabelURLGenerator);
2866: }
2867: return clone;
2868: }
2869:
2870: /**
2871: * Provides serialization support.
2872: *
2873: * @param stream the output stream.
2874: *
2875: * @throws IOException if there is an I/O error.
2876: */
2877: private void writeObject(ObjectOutputStream stream)
2878: throws IOException {
2879: stream.defaultWriteObject();
2880: SerialUtilities.writePaint(this .sectionPaint, stream);
2881: SerialUtilities.writePaint(this .baseSectionPaint, stream);
2882: SerialUtilities.writePaint(this .sectionOutlinePaint, stream);
2883: SerialUtilities
2884: .writePaint(this .baseSectionOutlinePaint, stream);
2885: SerialUtilities.writeStroke(this .sectionOutlineStroke, stream);
2886: SerialUtilities.writeStroke(this .baseSectionOutlineStroke,
2887: stream);
2888: SerialUtilities.writePaint(this .shadowPaint, stream);
2889: SerialUtilities.writePaint(this .labelPaint, stream);
2890: SerialUtilities.writePaint(this .labelBackgroundPaint, stream);
2891: SerialUtilities.writePaint(this .labelOutlinePaint, stream);
2892: SerialUtilities.writeStroke(this .labelOutlineStroke, stream);
2893: SerialUtilities.writePaint(this .labelShadowPaint, stream);
2894: SerialUtilities.writePaint(this .labelLinkPaint, stream);
2895: SerialUtilities.writeStroke(this .labelLinkStroke, stream);
2896: SerialUtilities.writeShape(this .legendItemShape, stream);
2897: }
2898:
2899: /**
2900: * Provides serialization support.
2901: *
2902: * @param stream the input stream.
2903: *
2904: * @throws IOException if there is an I/O error.
2905: * @throws ClassNotFoundException if there is a classpath problem.
2906: */
2907: private void readObject(ObjectInputStream stream)
2908: throws IOException, ClassNotFoundException {
2909: stream.defaultReadObject();
2910: this .sectionPaint = SerialUtilities.readPaint(stream);
2911: this .baseSectionPaint = SerialUtilities.readPaint(stream);
2912: this .sectionOutlinePaint = SerialUtilities.readPaint(stream);
2913: this .baseSectionOutlinePaint = SerialUtilities
2914: .readPaint(stream);
2915: this .sectionOutlineStroke = SerialUtilities.readStroke(stream);
2916: this .baseSectionOutlineStroke = SerialUtilities
2917: .readStroke(stream);
2918: this .shadowPaint = SerialUtilities.readPaint(stream);
2919: this .labelPaint = SerialUtilities.readPaint(stream);
2920: this .labelBackgroundPaint = SerialUtilities.readPaint(stream);
2921: this .labelOutlinePaint = SerialUtilities.readPaint(stream);
2922: this .labelOutlineStroke = SerialUtilities.readStroke(stream);
2923: this .labelShadowPaint = SerialUtilities.readPaint(stream);
2924: this .labelLinkPaint = SerialUtilities.readPaint(stream);
2925: this .labelLinkStroke = SerialUtilities.readStroke(stream);
2926: this .legendItemShape = SerialUtilities.readShape(stream);
2927: }
2928:
2929: // DEPRECATED METHODS...
2930:
2931: /**
2932: * Returns the paint for the specified section.
2933: *
2934: * @param section the section index (zero-based).
2935: *
2936: * @return The paint (never <code>null</code>).
2937: *
2938: * @deprecated Use {@link #getSectionPaint(Comparable)} instead.
2939: */
2940: public Paint getSectionPaint(int section) {
2941: Comparable key = getSectionKey(section);
2942: return getSectionPaint(key);
2943: }
2944:
2945: /**
2946: * Sets the paint used to fill a section of the pie and sends a
2947: * {@link PlotChangeEvent} to all registered listeners.
2948: *
2949: * @param section the section index (zero-based).
2950: * @param paint the paint (<code>null</code> permitted).
2951: *
2952: * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} instead.
2953: */
2954: public void setSectionPaint(int section, Paint paint) {
2955: Comparable key = getSectionKey(section);
2956: setSectionPaint(key, paint);
2957: }
2958:
2959: /**
2960: * Returns the paint for the specified section.
2961: *
2962: * @param section the section index (zero-based).
2963: *
2964: * @return The paint (possibly <code>null</code>).
2965: *
2966: * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} instead.
2967: */
2968: public Paint getSectionOutlinePaint(int section) {
2969: Comparable key = getSectionKey(section);
2970: return getSectionOutlinePaint(key);
2971: }
2972:
2973: /**
2974: * Sets the paint used to fill a section of the pie and sends a
2975: * {@link PlotChangeEvent} to all registered listeners.
2976: *
2977: * @param section the section index (zero-based).
2978: * @param paint the paint (<code>null</code> permitted).
2979: *
2980: * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)}
2981: * instead.
2982: */
2983: public void setSectionOutlinePaint(int section, Paint paint) {
2984: Comparable key = getSectionKey(section);
2985: setSectionOutlinePaint(key, paint);
2986: }
2987:
2988: /**
2989: * Returns the stroke for the specified section.
2990: *
2991: * @param section the section index (zero-based).
2992: *
2993: * @return The stroke (possibly <code>null</code>).
2994: *
2995: * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} instead.
2996: */
2997: public Stroke getSectionOutlineStroke(int section) {
2998: Comparable key = getSectionKey(section);
2999: return getSectionOutlineStroke(key);
3000: }
3001:
3002: /**
3003: * Sets the stroke used to fill a section of the pie and sends a
3004: * {@link PlotChangeEvent} to all registered listeners.
3005: *
3006: * @param section the section index (zero-based).
3007: * @param stroke the stroke (<code>null</code> permitted).
3008: *
3009: * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)}
3010: * instead.
3011: */
3012: public void setSectionOutlineStroke(int section, Stroke stroke) {
3013: Comparable key = getSectionKey(section);
3014: setSectionOutlineStroke(key, stroke);
3015: }
3016:
3017: /**
3018: * Returns the amount that a section should be 'exploded'.
3019: *
3020: * @param section the section number.
3021: *
3022: * @return The amount that a section should be 'exploded'.
3023: *
3024: * @deprecated Use {@link #getExplodePercent(Comparable)} instead.
3025: */
3026: public double getExplodePercent(int section) {
3027: Comparable key = getSectionKey(section);
3028: return getExplodePercent(key);
3029: }
3030:
3031: /**
3032: * Sets the amount that a pie section should be exploded and sends a
3033: * {@link PlotChangeEvent} to all registered listeners.
3034: *
3035: * @param section the section index.
3036: * @param percent the explode percentage (0.30 = 30 percent).
3037: *
3038: * @deprecated Use {@link #setExplodePercent(Comparable, double)} instead.
3039: */
3040: public void setExplodePercent(int section, double percent) {
3041: Comparable key = getSectionKey(section);
3042: setExplodePercent(key, percent);
3043: }
3044:
3045: }
|