Package com.positive.charts.plot

Source Code of com.positive.charts.plot.PiePlot

/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
*
* Project Info:  http://www.jfree.org/jfreechart/index.html
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
* USA. 
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------
* PiePlot.java
* ------------
* (C) Copyright 2000-2008, by Andrzej Porebski and Contributors.
*
* Original Author:  Andrzej Porebski;
* Contributor(s):   David Gilbert (for Object Refinery Limited);
*                   Martin Cordova (percentages in labels);
*                   Richard Atkinson (URL support for image maps);
*                   Christian W. Zuckschwerdt;
*                   Arnaud Lelievre;
*                   Martin Hilpert (patch 1891849);
*                   Andreas Schroeder (very minor);
*
* Changes
* -------
* 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
* 18-Sep-2001 : Updated header (DG);
* 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
* 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to
*               Plot.java (DG);
* 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
* 13-Nov-2001 : Modified plot subclasses so that null axes are possible for
*               pie plot (DG);
* 17-Nov-2001 : Added PieDataset interface and amended this class accordingly,
*               and completed removal of BlankAxis class as it is no longer
*               required (DG);
* 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG);
* 21-Nov-2001 : Added options for exploding pie sections and filled out range
*               of properties (DG);
*               Added option for percentages in chart labels, based on code
*               by Martin Cordova (DG);
* 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG);
* 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG);
* 13-Dec-2001 : Added tooltips (DG);
* 16-Jan-2002 : Renamed tooltips class (DG);
* 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
* 05-Feb-2002 : Added alpha-transparency to plot class, and updated
*               constructors accordingly (DG);
* 06-Feb-2002 : Added optional background image and alpha-transparency to Plot
*               and subclasses.  Clipped drawing within plot area (DG);
* 26-Mar-2002 : Added an empty zoom method (DG);
* 18-Apr-2002 : PieDataset is no longer sorted (oldman);
* 23-Apr-2002 : Moved dataset from JFreeChart to Plot.  Added
*               getLegendItemLabels() method (DG);
* 19-Jun-2002 : Added attributes to control starting angle and direction
*               (default is now clockwise) (DG);
* 25-Jun-2002 : Removed redundant imports (DG);
* 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG);
* 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG);
* 30-Jul-2002 : Moved summation code to DatasetUtilities (DG);
* 05-Aug-2002 : Added URL support for image maps - new member variable for
*               urlGenerator, modified constructor and minor change to the
*               draw method (RA);
* 18-Sep-2002 : Modified the percent label creation and added setters for the
*               formatters (AS);
* 24-Sep-2002 : Added getLegendItems() method (DG);
* 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
* 09-Oct-2002 : Added check for null entity collection (DG);
* 30-Oct-2002 : Changed PieDataset interface (DG);
* 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG);
* 02-Jan-2003 : Fixed "no data" message (DG);
* 23-Jan-2003 : Modified to extract data from rows OR columns in
*               CategoryDataset (DG);
* 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply
*               (bug id 685536) (DG);
* 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip
*               and URL generators (DG);
* 21-Mar-2003 : Added a minimum angle for drawing arcs
*               (see bug id 620031) (DG);
* 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG);
* 02-Jun-2003 : Fixed bug 721733 (DG);
* 30-Jul-2003 : Modified entity constructor (CZ);
* 19-Aug-2003 : Implemented Cloneable (DG);
* 29-Aug-2003 : Fixed bug 796936 (null pointer on setOutlinePaint()) (DG);
* 08-Sep-2003 : Added internationalization via use of properties
*               resourceBundle (RFE 690236) (AL);
* 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
* 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
* 05-Nov-2003 : Fixed missing legend bug (DG);
* 10-Nov-2003 : Re-added the DatasetChangeListener to constructors (CZ);
* 29-Jan-2004 : Fixed clipping bug in draw() method (DG);
* 11-Mar-2004 : Major overhaul to improve labelling (DG);
* 31-Mar-2004 : Made an adjustment for the plot area when the label generator
*               is null.  Fixed null pointer exception when the label
*               generator returns null for a label (DG);
* 06-Apr-2004 : Added getter, setter, serialization and draw support for
*               labelBackgroundPaint (AS);
* 08-Apr-2004 : Added flag to control whether null values are ignored or
*               not (DG);
* 15-Apr-2004 : Fixed some minor warnings from Eclipse (DG);
* 26-Apr-2004 : Added attributes for label outline and shadow (DG);
* 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG);
* 04-Nov-2004 : Fixed null pointer exception with new LegendTitle class (DG);
* 09-Nov-2004 : Added user definable legend item shape (DG);
* 25-Nov-2004 : Added new legend label generator (DG);
* 20-Apr-2005 : Added a tool tip generator for legend labels (DG);
* 26-Apr-2005 : Removed LOGGER (DG);
* 05-May-2005 : Updated draw() method parameters (DG);
* 10-May-2005 : Added flag to control visibility of label linking lines, plus
*               another flag to control the handling of zero values (DG);
* 08-Jun-2005 : Fixed bug in getLegendItems() method (not respecting flags
*               for ignoring null and zero values), and fixed equals() method
*               to handle GradientPaint (DG);
* 15-Jul-2005 : Added sectionOutlinesVisible attribute (DG);
* ------------- JFREECHART 1.0.x ---------------------------------------------
* 09-Jan-2006 : Fixed bug 1400442, inconsistent treatment of null and zero
*               values in dataset (DG);
* 28-Feb-2006 : Fixed bug 1440415, bad distribution of pie section
*               labels (DG);
* 27-Sep-2006 : Initialised baseSectionPaint correctly, added lookup methods
*               for section paint, outline paint and outline stroke (DG);
* 27-Sep-2006 : Refactored paint and stroke methods to use keys rather than
*               section indices (DG);
* 03-Oct-2006 : Replaced call to JRE 1.5 method (DG);
* 23-Nov-2006 : Added support for URLs for the legend items (DG);
* 24-Nov-2006 : Cloning fixes (DG);
* 17-Apr-2007 : Check for null label in legend items (DG);
* 19-Apr-2007 : Deprecated override settings (DG);
* 18-May-2007 : Set dataset for LegendItem (DG);
* 14-Jun-2007 : Added label distributor attribute (DG);
* 18-Jul-2007 : Added simple label option (DG);
* 21-Nov-2007 : Fixed labelling bugs, added debug code, restored default
*               white background (DG);
* 19-Mar-2008 : Fixed IllegalArgumentException when drawing with null
*               dataset (DG);
* 31-Mar-2008 : Adjust the label area for the interiorGap (DG);
* 31-Mar-2008 : Added quad and cubic curve label link lines - see patch
*               1891849 by Martin Hilpert (DG);
*   
*/

package com.positive.charts.plot;

import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.TreeMap;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Display;

import com.positive.charts.block.RectangleInsets;
import com.positive.charts.block.RectangleInsets.UnitType;
import com.positive.charts.data.DefaultKeyedValues;
import com.positive.charts.data.KeyedValues;
import com.positive.charts.data.general.PieDataset;
import com.positive.charts.data.util.DatasetUtilities;
import com.positive.charts.data.util.ObjectUtilities;
import com.positive.charts.data.util.PublicCloneable;
import com.positive.charts.data.util.RectangleAnchor;
import com.positive.charts.entity.EntityCollection;
import com.positive.charts.entity.PieSectionEntity;
import com.positive.charts.event.DatasetChangeEvent;
import com.positive.charts.event.PlotChangeEvent;
import com.positive.charts.labels.PieSectionLabelGenerator;
import com.positive.charts.labels.PieToolTipGenerator;
import com.positive.charts.labels.StandardPieSectionLabelGenerator;
import com.positive.charts.legend.LegendItem;
import com.positive.charts.legend.LegendItemCollection;
import com.positive.charts.text.TextBox;
import com.positive.charts.urls.PieURLGenerator;
import com.positive.charts.util.RectangleUtil;
import com.positive.charts.util.Rotation;
import com.positive.charts.util.Shape;
import com.positive.charts.util.Stroke;
import com.positive.charts.util.TextAnchor;
import com.positive.charts.util.TextBlock;
import com.positive.charts.util.TextUtilities;
import com.positive.colorchecker.StaticColorChecker;

/**
* A plot that displays data in the form of a pie chart, using data from any
* class that implements the {@link PieDataset} interface.
* <P>
* Special notes:
* <ol>
* <li>the default starting point is 12 o'clock and the pie sections proceed in
* a clockwise direction, but these settings can be changed;</li>
* <li>negative values in the dataset are ignored;</li>
* <li>there are utility methods for creating a {@link PieDataset} from a
* {@link org.jfree.data.category.CategoryDataset};</li>
* </ol>
*
* @see Plot
* @see PieDataset
*/
public class PiePlot extends Plot implements Cloneable, Serializable {

  /** For serialization. */
  private static final long serialVersionUID = -795612466005590431L;

  /** The default interior gap. */
  public static final double DEFAULT_INTERIOR_GAP = 0.08;

  /** The maximum interior gap (currently 40%). */
  public static final double MAX_INTERIOR_GAP = 0.40;

  /** The default starting angle for the pie chart. */
  public static final double DEFAULT_START_ANGLE = 90.0;

  /** The default section label font. */
  public static final Font DEFAULT_LABEL_FONT = new Font(
      Display.getDefault(), "SansSerif", 8, SWT.NONE);

  /** The default section label Color. */
  public static final Color DEFAULT_LABEL_PAINT = StaticColorChecker
      .dublicateColor(SWT.COLOR_BLACK);
  // Display.getDefault()
  // .getSystemColor(SWT.COLOR_BLACK);

  /** The default section label background Color. */
  public static final Color DEFAULT_LABEL_BACKGROUND_PAINT = new Color(
      Display.getDefault(), 255, 255, 192);

  /** The default section label outline Color. */
  public static final Color DEFAULT_LABEL_OUTLINE_PAINT = StaticColorChecker
      .dublicateColor(SWT.COLOR_BLACK);// Display
  // .getDefault().getSystemColor(SWT.COLOR_BLACK);

  /** The default section label outline stroke. */
  public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new Stroke(1);

  /** The default section label shadow Color. */
  public static final Color DEFAULT_LABEL_SHADOW_PAINT = new Color(
      Display.getDefault(), 151, 151, 151);

  /** The default minimum arc angle to draw. */
  public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001;

  /** The dataset for the pie chart. */
  private PieDataset dataset;

  /** The pie index (used by the {@link MultiplePiePlot} class). */
  private int pieIndex;

  /**
   * The amount of space left around the outside of the pie plot, expressed as
   * a percentage of the plot area width and height.
   */
  private double interiorGap;

  /** Flag determining whether to draw an ellipse or a perfect circle. */
  private boolean circular;

  /** The starting angle. */
  private double startAngle;

  /** The direction for the pie segments. */
  private Rotation direction;

  /**
   * The Color for ALL sections (overrides list).
   *
   * @deprecated This field is redundant, it is sufficient to use
   *             sectionPaintMap and baseSectionPaint. Deprecated as of
   *             version 1.0.6.
   */
  private transient Color sectionPaint;

  /** The section Color map. */
  private final HashMap sectionPaintMap = new HashMap();

  /** The base section Color (fallback). */
  private transient Color baseSectionPaint;

  /**
   * A flag that controls whether or not an outline is drawn for each section
   * in the plot.
   */
  private boolean sectionOutlinesVisible;

  /**
   * The outline Color for ALL sections (overrides list).
   *
   * @deprecated This field is redundant, it is sufficient to use
   *             sectionOutlinePaintMap and baseSectionOutlinePaint.
   *             Deprecated as of version 1.0.6.
   */
  private transient Color sectionOutlinePaint;

  /** The section outline Color map. */
  // private PaintMap sectionOutlinePaintMap;
  /** The base section outline Color (fallback). */
  private transient Color baseSectionOutlinePaint;

  /**
   * The outline stroke for ALL sections (overrides list).
   *
   * @deprecated This field is redundant, it is sufficient to use
   *             sectionOutlineStrokeMap and baseSectionOutlineStroke.
   *             Deprecated as of version 1.0.6.
   */
  // private transient Stroke sectionOutlineStroke;
  /** The section outline stroke map. */
  // private StrokeMap sectionOutlineStrokeMap;
  /** The base section outline stroke (fallback). */
  // private transient Stroke baseSectionOutlineStroke;
  /** The shadow Color. */
  private transient Color shadowPaint =  StaticColorChecker.dublicateColor(SWT.COLOR_GRAY);
    //Display.getDefault().getSystemColor(
    //  SWT.COLOR_GRAY);

  /** The x-offset for the shadow effect. */
  private double shadowXOffset = 4.0f;

  /** The y-offset for the shadow effect. */
  private double shadowYOffset = 4.0f;

  /** The percentage amount to explode each pie section. */
  private Map explodePercentages;

  /** The section label generator. */
  private PieSectionLabelGenerator labelGenerator;

  /** The font used to display the section labels. */
  private Font labelFont;

  /** The color used to draw the section labels. */
  private transient Color labelPaint;

  /**
   * The color used to draw the background of the section labels. If this is
   * <code>null</code>, the background is not filled.
   */
  private transient Color labelBackgroundPaint;

  /**
   * The Color used to draw the outline of the section labels (
   * <code>null</code> permitted).
   */
  private transient Color labelOutlinePaint;

  /**
   * The stroke used to draw the outline of the section labels (
   * <code>null</code> permitted).
   */
  // private transient Stroke labelOutlineStroke;
  /**
   * The Color used to draw the shadow for the section labels (
   * <code>null</code> permitted).
   */
  private transient Color labelShadowPaint;

  /**
   * A flag that controls whether simple or extended labels are used.
   *
   * @since 1.0.7
   */
  private boolean simpleLabels = true;

  /**
   * The padding between the labels and the label outlines. This is not
   * allowed to be <code>null</code>.
   *
   * @since 1.0.7
   */
  private RectangleInsets labelPadding;

  /**
   * The simple label offset.
   *
   * @since 1.0.7
   */
  private RectangleInsets simpleLabelOffset;

  /** The maximum label width as a percentage of the plot width. */
  private double maximumLabelWidth = 0.14;

  /**
   * The gap between the labels and the link corner, as a percentage of the
   * plot width.
   */
  private double labelGap = 0.025;

  /** A flag that controls whether or not the label links are drawn. */
  private boolean labelLinksVisible;

  /**
   * The label link style.
   *
   * @since 1.0.10
   */
  private PieLabelLinkStyle labelLinkStyle = PieLabelLinkStyle.STANDARD;

  /** The link margin. */
  private double labelLinkMargin = 0.025;

  /** The Color used for the label linking lines. */
  private transient Color labelLinkPaint = StaticColorChecker
      .dublicateColor(SWT.COLOR_BLACK);
  // Display.getDefault()
  // .getSystemColor(SWT.COLOR_BLACK);

  /** The stroke used for the label linking lines. */
  private transient Stroke labelLinkStroke = new Stroke(1);

  /**
   * The pie section label distributor.
   *
   * @since 1.0.6
   */
  private AbstractPieLabelDistributor labelDistributor;

  /** The tooltip generator. */
  private PieToolTipGenerator toolTipGenerator;

  /** The URL generator. */
  private PieURLGenerator urlGenerator;

  /** The legend label generator. */
  private PieSectionLabelGenerator legendLabelGenerator;

  /** A tool tip generator for the legend. */
  private PieSectionLabelGenerator legendLabelToolTipGenerator;

  /**
   * A URL generator for the legend items (optional).
   *
   * @since 1.0.4.
   */
  private PieURLGenerator legendLabelURLGenerator;

  /**
   * A flag that controls whether <code>null</code> values are ignored.
   */
  private boolean ignoreNullValues;

  /**
   * A flag that controls whether zero values are ignored.
   */
  private boolean ignoreZeroValues;

  /** The legend item shape. */
  private transient Region legendItemShape;

  /**
   * The smallest arc angle that will get drawn (this is to avoid a bug in
   * various Java implementations that causes the JVM to crash). See this link
   * for details:
   *
   * http://www.jfree.org/phpBB2/viewtopic.php?t=2707
   *
   * ...and this bug report in the Java Bug Parade:
   *
   * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html
   */
  private double minimumArcAngleToDraw;

  private Stroke baseSectionOutlineStroke;

  private Stroke labelOutlineStroke;

  private final HashMap sectionOutlinePaintMap = new HashMap();

  private Stroke sectionOutlineStroke;

  private final HashMap sectionOutlineStrokeMap = new HashMap();

  /** The resourceBundle for the localization. */
  protected static ResourceBundle localizationResources = ResourceBundle
      .getBundle("com.positive.charts.plot.LocalizationBundle");

  /**
   * This debug flag controls whether or not an outline is drawn showing the
   * interior of the plot region. This is drawn as a lightGray rectangle
   * showing the padding provided by the 'interiorGap' setting.
   */
  static final boolean DEBUG_DRAW_INTERIOR = false;

  /**
   * This debug flag controls whether or not an outline is drawn showing the
   * link area (in blue) and link ellipse (in yellow). This controls where the
   * label links have 'elbow' points.
   */
  static final boolean DEBUG_DRAW_LINK_AREA = false;

  /**
   * This debug flag controls whether or not an outline is drawn showing the
   * pie area (in green).
   */
  static final boolean DEBUG_DRAW_PIE_AREA = false;

  /**
   * Creates a new plot. The dataset is initially set to <code>null</code>.
   */
  public PiePlot() {
    this(null);
  }

  /**
   * Creates a plot that will draw a pie chart for the specified dataset.
   *
   * @param dataset
   *            the dataset (<code>null</code> permitted).
   */
  public PiePlot(final PieDataset dataset) {
    super();
    this.dataset = dataset;
    if (dataset != null) {
      dataset.addChangeListener(this);
    }
    this.pieIndex = 0;

    this.interiorGap = DEFAULT_INTERIOR_GAP;
    this.circular = true;
    this.startAngle = DEFAULT_START_ANGLE;
    this.direction = Rotation.CLOCKWISE;
    this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW;

    this.sectionPaint = null;
    // this.sectionPaintMap = new PaintMap();
    this.baseSectionPaint = StaticColorChecker
        .dublicateColor(SWT.COLOR_GRAY);
    // Display.getCurrent().getSystemColor(
    // SWT.COLOR_GRAY);

    this.sectionOutlinesVisible = true;
    this.sectionOutlinePaint = null;
    // this.sectionOutlinePaintMap = new PaintMap();
    // this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT;

    // this.sectionOutlineStroke = null;
    // this.sectionOutlineStrokeMap = new StrokeMap();
    // this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE;

    this.explodePercentages = new TreeMap();

    this.labelGenerator = new StandardPieSectionLabelGenerator();
    this.labelFont = DEFAULT_LABEL_FONT;
    this.labelPaint = DEFAULT_LABEL_PAINT;
    this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT;
    this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT;
    this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE;
    this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT;
    this.labelLinksVisible = true;
    this.labelDistributor = new PieLabelDistributor(0);

    this.simpleLabels = false;
    this.simpleLabelOffset = new RectangleInsets(UnitType.ABSOLUTE, 1, 1,
        1, 1);
    this.labelPadding = new RectangleInsets(2, 2, 2, 2);

    this.toolTipGenerator = null;
    this.urlGenerator = null;
    this.legendLabelGenerator = new StandardPieSectionLabelGenerator();
    this.legendLabelToolTipGenerator = null;
    this.legendLabelURLGenerator = null;
    // this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE;

    this.ignoreNullValues = false;
    this.ignoreZeroValues = false;
  }

  /**
   * Returns a clone of the plot.
   *
   * @return A clone.
   *
   * @throws CloneNotSupportedException
   *             if some component of the plot does not support cloning.
   */
  public Object clone() throws CloneNotSupportedException {
    final PiePlot clone = (PiePlot) super.clone();
    if (clone.dataset != null) {
      clone.dataset.addChangeListener(clone);
    }
    if (this.urlGenerator instanceof PublicCloneable) {
      clone.urlGenerator = (PieURLGenerator) ObjectUtilities
          .clone(this.urlGenerator);
    }
    // clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape);
    if (this.legendLabelGenerator != null) {
      clone.legendLabelGenerator = (PieSectionLabelGenerator) ObjectUtilities
          .clone(this.legendLabelGenerator);
    }
    if (this.legendLabelToolTipGenerator != null) {
      clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator) ObjectUtilities
          .clone(this.legendLabelToolTipGenerator);
    }
    if (this.legendLabelURLGenerator instanceof PublicCloneable) {
      clone.legendLabelURLGenerator = (PieURLGenerator) ObjectUtilities
          .clone(this.legendLabelURLGenerator);
    }
    return clone;
  }

  /**
   * Draws the plot on a Java 2D graphics device (such as the screen or a
   * printer).
   *
   * @param g2
   *            the graphics device.
   * @param area
   *            the area within which the plot should be drawn.
   * @param anchor
   *            the anchor point (<code>null</code> permitted).
   * @param parentState
   *            the state from the parent plot, if there is one.
   * @param info
   *            collects info about the drawing (<code>null</code> permitted).
   */
  public void draw(final GC g2, final Rectangle area, final Point anchor,
      final PlotState parentState, final PlotRenderingInfo info) {

    // adjust for insets...
    g2.setAdvanced(true);
    g2.setAntialias(SWT.ON);
    final RectangleInsets insets = this.getInsets();
    insets.trim(area);

    if (info != null) {
      info.setPlotArea(area);
      info.setDataArea(area);
    }

    this.drawBackground(g2, area);
    this.drawOutline(g2, area);

    // Shape savedClip = g2.getClip();
    // g2.clip(area);

    // Composite originalComposite = g2.getComposite();
    // g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    // getForegroundAlpha()));

    if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
      this.drawPie(g2, area, info);
    } else {
      // drawNoDataMessage(g2, area);
    }

    // g2.setClip(savedClip);
    // g2.setComposite(originalComposite);

    this.drawOutline(g2, area);

  }

  /**
   * Draws a single data item.
   *
   * @param g2
   *            the graphics device (<code>null</code> not permitted).
   * @param section
   *            the section index.
   * @param dataArea
   *            the data plot area.
   * @param state
   *            state information for one chart.
   * @param currentPass
   *            the current pass index.
   */
  protected void drawItem(final GC g2, final int section,
      final Rectangle dataArea, final PiePlotState state,
      final int currentPass) {

    final Number n = this.dataset.getValue(section);
    if (n == null) {
      return;
    }
    final double value = n.doubleValue();
    double angle1 = 0.0;
    double angle2 = 0.0;

    if (this.direction == Rotation.CLOCKWISE) {
      angle1 = state.getLatestAngle();
      angle2 = angle1 - value / state.getTotal() * 360.0;
    } else if (this.direction == Rotation.ANTICLOCKWISE) {
      angle1 = state.getLatestAngle();
      angle2 = angle1 + value / state.getTotal() * 360.0;
    } else {
      throw new IllegalStateException("Rotation type not recognised.");
    }

    final double angle = (angle2 - angle1);
    if (Math.abs(angle) > this.getMinimumArcAngleToDraw()) {
      double ep = 0.0;
      final double mep = this.getMaximumExplodePercent();
      if (mep > 0.0) {
        ep = this.getExplodePercent(section) / mep;
      }
      final Rectangle arcBounds = this.getArcBounds(state.getPieArea(),
          state.getExplodedPieArea(), angle1, angle, ep);

      if (currentPass == 0) {
        if (this.shadowPaint != null) {

          final Color Color = this.shadowPaint;
          g2.setBackground(Color);
          g2.fillArc((int) (arcBounds.x + this.shadowXOffset),
              (int) (arcBounds.y + this.shadowYOffset),
              arcBounds.width, arcBounds.height,
              (int) angle1 + 1, (int) angle - 1);

        }
      } else if (currentPass == 1) {
        final Comparable key = this.getSectionKey(section);
        final Color Color = this.lookupSectionPaint(key, true);
        g2.setBackground(Color);
        g2.fillArc(arcBounds.x, arcBounds.y, arcBounds.width,
            arcBounds.height, (int) angle1 + 1, (int) angle - 1);

        final Color outlinePaint = this.lookupSectionOutlinePaint(key);
        final Stroke outlineStroke = this
            .lookupSectionOutlineStroke(key);
        if (this.sectionOutlinesVisible) {
          g2.setForeground(outlinePaint);
          outlineStroke.set(g2);
          g2.drawArc(arcBounds.x, arcBounds.y, arcBounds.width,
              arcBounds.height, (int) angle1 + 1, (int) angle - 1);
        }

        // update the linking line target for later
        // add an entity for the pie section
        if (state.getInfo() != null) {
          final EntityCollection entities = state
              .getEntityCollection();
          if (entities != null) {
            final String tip = null;
            if (this.toolTipGenerator != null) {
              // tip = this.toolTipGenerator.generateToolTip(
              // this.dataset, key);
            }
            String url = null;
            if (this.urlGenerator != null) {
              url = this.urlGenerator.generateURL(this.dataset,
                  key, this.pieIndex);
            }
            final PieSectionEntity entity = new PieSectionEntity(
                null, this.dataset, this.pieIndex, section,
                key, tip, url);
            entities.add(entity);
          }
        }
      }
    }
    state.setLatestAngle(angle2);
  }

  /**
   * Draws the labels for the pie sections.
   *
   * @param g2
   *            the graphics device.
   * @param keys
   *            the keys.
   * @param totalValue
   *            the total value.
   * @param plotArea
   *            the plot area.
   * @param linkArea
   *            the link area.
   * @param state
   *            the state.
   */
  protected void drawLabels(final GC g2, final List keys,
      final double totalValue, final Rectangle plotArea,
      final Rectangle linkArea, final PiePlotState state) {

    // Composite originalComposite = g2.getComposite();
    // g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    // 1.0f));

    // classify the keys according to which side the label will appear...
    final DefaultKeyedValues leftKeys = new DefaultKeyedValues();
    final DefaultKeyedValues rightKeys = new DefaultKeyedValues();

    double runningTotal = 0.0;
    final Iterator iterator = keys.iterator();
    while (iterator.hasNext()) {
      final Comparable key = (Comparable) iterator.next();
      boolean include = true;
      double v = 0.0;
      final Number n = this.dataset.getValue(key);
      if (n == null) {
        include = !this.ignoreNullValues;
      } else {
        v = n.doubleValue();
        include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0;
      }

      if (include) {
        runningTotal = runningTotal + v;
        // work out the mid angle (0 - 90 and 270 - 360) = right,
        // otherwise left
        final double mid = this.startAngle
            + (this.direction.getFactor()
                * ((runningTotal - v / 2.0) * 360) / totalValue);
        if (Math.cos(Math.toRadians(mid)) < 0.0) {
          leftKeys.addValue(key, new Double(mid));
        } else {
          rightKeys.addValue(key, new Double(mid));
        }
      }
    }

    g2.setFont(this.getLabelFont());

    // calculate the max label width from the plot dimensions, because
    // a circular pie can leave a lot more room for labels...
    final double marginX = plotArea.x + this.interiorGap * plotArea.width;
    final double gap = plotArea.width * this.labelGap;
    final double ww = linkArea.x - gap - marginX;
    final float labelWidth = this.labelPadding.trimWidth((int) ww);

    // draw the labels...
    if (this.labelGenerator != null) {
      this.drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth,
          state);
      this.drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth,
          state);
    }
    // g2.setComposite(originalComposite);

  }

  /**
   * Draws a section label on the left side of the pie chart.
   *
   * @param g2
   *            the graphics device.
   * @param state
   *            the state.
   * @param record
   *            the label record.
   */
  protected void drawLeftLabel(final GC g2, final PiePlotState state,
      final PieLabelRecord record) {

    final double anchorX = RectangleUtil.getMinX(state.getLinkArea());
    final double targetX = anchorX - record.getGap();
    final double targetY = record.getAllocatedY();

    if (this.labelLinksVisible) {
      final double theta = record.getAngle();
      final double linkX = state.getPieCenterX() + Math.cos(theta)
          * state.getPieWRadius() * record.getLinkPercent();
      final double linkY = state.getPieCenterY() - Math.sin(theta)
          * state.getPieHRadius() * record.getLinkPercent();
      final double elbowX = state.getPieCenterX() + Math.cos(theta)
          * state.getLinkArea().width / 2.0;
      final double elbowY = state.getPieCenterY() - Math.sin(theta)
          * state.getLinkArea().height / 2.0;
      final double anchorY = elbowY;
      g2.setForeground(this.labelLinkPaint);
      this.labelLinkStroke.set(g2);
      // g2.setStroke(this.labelLinkStroke);
      final PieLabelLinkStyle style = this.getLabelLinkStyle();
      if (style.equals(PieLabelLinkStyle.STANDARD)) {
        g2.drawLine((int) linkX, (int) linkY, (int) elbowX,
            (int) elbowY);
        g2.drawLine((int) anchorX, (int) anchorY, (int) elbowX,
            (int) elbowY);
        g2.drawLine((int) anchorX, (int) anchorY, (int) targetX,
            (int) targetY);
      }

    }
    final TextBox tb = record.getLabel();
    tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT);

  }

  /**
   * Draws the left labels.
   *
   * @param leftKeys
   *            a collection of keys and angles (to the middle of the section,
   *            in degrees) for the sections on the left side of the plot.
   * @param g2
   *            the graphics device.
   * @param plotArea
   *            the plot area.
   * @param linkArea
   *            the link area.
   * @param maxLabelWidth
   *            the maximum label width.
   * @param state
   *            the state.
   */
  protected void drawLeftLabels(final KeyedValues leftKeys, final GC g2,
      final Rectangle plotArea, final Rectangle linkArea,
      final float maxLabelWidth, final PiePlotState state) {

    this.labelDistributor.clear();
    final double lGap = plotArea.width * this.labelGap;
    final double verticalLinkRadius = state.getLinkArea().height / 2.0;
    for (int i = 0; i < leftKeys.getItemCount(); i++) {
      final String label = this.labelGenerator.generateSectionLabel(
          this.dataset, leftKeys.getKey(i));
      if (label != null) {
        final TextBlock block = TextUtilities.createTextBlock(label,
            this.labelFont, this.labelPaint);
        final TextBox labelBox = new TextBox(block);
        labelBox.setBackgroundPaint(this.labelBackgroundPaint);
        labelBox.setOutlinePaint(this.labelOutlinePaint);
        labelBox.setOutlineStroke(this.labelOutlineStroke);
        labelBox.setShadowPaint(this.labelShadowPaint);
        labelBox.setInteriorGap(this.labelPadding);
        final double theta = Math.toRadians(leftKeys.getValue(i)
            .doubleValue());
        final double baseY = state.getPieCenterY() - Math.sin(theta)
            * verticalLinkRadius;
        final double hh = labelBox.getHeight(g2);

        this.labelDistributor.addPieLabelRecord(new PieLabelRecord(
            leftKeys.getKey(i), theta, baseY, labelBox, hh, lGap
                / 2.0 + lGap / 2.0 * -Math.cos(theta),
            0.9 + this.getExplodePercent(leftKeys.getKey(i))));
      }
    }
    final double hh = plotArea.height;
    final double gap = hh * this.getInteriorGap();
    this.labelDistributor.distributeLabels(RectangleUtil.getMinX(plotArea)
        + gap, hh - 2 * gap);
    for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
      this.drawLeftLabel(g2, state,
          this.labelDistributor.getPieLabelRecord(i));
    }
  }

  /**
   * Draws the pie.
   *
   * @param g2
   *            the graphics device.
   * @param plotArea
   *            the plot area.
   * @param info
   *            chart rendering info.
   */
  protected void drawPie(final GC g2, final Rectangle plotArea,
      final PlotRenderingInfo info) {

    final PiePlotState state = this.initialise(g2, plotArea, this, null,
        info);

    // adjust the plot area for interior spacing and labels...
    double labelReserve = 0.0;
    if ((this.labelGenerator != null) && !this.simpleLabels) {
      labelReserve = this.labelGap + this.maximumLabelWidth;
    }
    final double gapHorizontal = plotArea.width
        * (this.interiorGap + labelReserve) * 2.0;
    final double gapVertical = plotArea.height * this.interiorGap * 2.0;

    // if (DEBUG_DRAW_INTERIOR) {
    // double hGap = plotArea.width * this.interiorGap;
    // double vGap = plotArea.height * this.interiorGap;
    //
    // double igx1 = plotArea.x + hGap;
    // double igx2 = plotArea.getMaxX() - hGap;
    // double igy1 = plotArea.getY() + vGap;
    // double igy2 = plotArea.getMaxY() - vGap;
    // g2.setPaint(Color.gray);
    // g2.draw(new Rectangle.Double(igx1, igy1, igx2 - igx1,
    // igy2 - igy1));
    // }

    double linkX = plotArea.x + gapHorizontal / 2;
    double linkY = plotArea.y + gapVertical / 2;
    double linkW = plotArea.width - gapHorizontal;
    double linkH = plotArea.height - gapVertical;

    // make the link area a square if the pie chart is to be circular...
    if (this.circular) {
      final double min = Math.min(linkW, linkH) / 2;
      linkX = (linkX + linkX + linkW) / 2 - min;
      linkY = (linkY + linkY + linkH) / 2 - min;
      linkW = 2 * min;
      linkH = 2 * min;
    }

    // the link area defines the dog leg points for the linking lines to
    // the labels
    final Rectangle linkArea = new Rectangle((int) linkX, (int) linkY,
        (int) linkW, (int) linkH);
    state.setLinkArea(linkArea);

    // the explode area defines the max circle/ellipse for the exploded
    // pie sections. it is defined by shrinking the linkArea by the
    // linkMargin factor.
    double lm = 0.0;
    if (!this.simpleLabels) {
      lm = this.labelLinkMargin;
    }
    final double hh = linkArea.width * lm * 2.0;
    final double vv = linkArea.height * lm * 2.0;
    final Rectangle explodeArea = new Rectangle((int) (linkX + hh / 2.0),
        (int) (linkY + vv / 2.0), (int) (linkW - hh),
        (int) (linkH - vv));

    state.setExplodedPieArea(explodeArea);

    // the pie area defines the circle/ellipse for regular pie sections.
    // it is defined by shrinking the explodeArea by the explodeMargin
    // factor.
    final double maximumExplodePercent = this.getMaximumExplodePercent();
    final double percent = maximumExplodePercent
        / (1.0 + maximumExplodePercent);

    final double h1 = explodeArea.width * percent;
    final double v1 = explodeArea.height * percent;
    final Rectangle pieArea = new Rectangle(
        (int) (explodeArea.x + h1 / 2.0),
        (int) (explodeArea.y + v1 / 2.0),
        (int) (explodeArea.width - h1), (int) (explodeArea.height - v1));

    state.setPieArea(pieArea);
    state.setPieCenterX(RectangleUtil.getCenterX(pieArea));
    state.setPieCenterY(RectangleUtil.getCenterY(pieArea));
    state.setPieWRadius(pieArea.width / 2.0);
    state.setPieHRadius(pieArea.height / 2.0);

    // plot the data (unless the dataset is null)...
    if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) {

      final List keys = this.dataset.getKeys();
      final double totalValue = DatasetUtilities
          .calculatePieDatasetTotal(this.dataset);

      final int passesRequired = state.getPassesRequired();
      for (int pass = 0; pass < passesRequired; pass++) {
        double runningTotal = 0.0;
        for (int section = 0; section < keys.size(); section++) {
          final Number n = this.dataset.getValue(section);
          if (n != null) {
            final double value = n.doubleValue();
            if (value > 0.0) {
              runningTotal += value;
              this.drawItem(g2, section, explodeArea, state, pass);
            }
          }
        }
      }
      if (this.simpleLabels) {
        this.drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea,
            state);
      } else {
        this.drawLabels(g2, keys, totalValue, plotArea, linkArea, state);
      }

    } else {
      // drawNoDataMessage(g2, plotArea);
    }
  }

  /**
   * Draws a section label on the right side of the pie chart.
   *
   * @param g2
   *            the graphics device.
   * @param state
   *            the state.
   * @param record
   *            the label record.
   */
  protected void drawRightLabel(final GC g2, final PiePlotState state,
      final PieLabelRecord record) {

    final double anchorX = RectangleUtil.getMaxX(state.getLinkArea());
    final double targetX = anchorX + record.getGap();
    final double targetY = record.getAllocatedY();

    if (this.labelLinksVisible) {
      final double theta = record.getAngle();
      final double linkX = state.getPieCenterX() + Math.cos(theta)
          * state.getPieWRadius() * record.getLinkPercent();
      final double linkY = state.getPieCenterY() - Math.sin(theta)
          * state.getPieHRadius() * record.getLinkPercent();
      final double elbowX = state.getPieCenterX() + Math.cos(theta)
          * state.getLinkArea().width / 2.0;
      final double elbowY = state.getPieCenterY() - Math.sin(theta)
          * state.getLinkArea().height / 2.0;
      final double anchorY = elbowY;
      g2.setForeground(this.labelLinkPaint);
      this.labelLinkStroke.set(g2);
      final PieLabelLinkStyle style = this.getLabelLinkStyle();
      if (style.equals(PieLabelLinkStyle.STANDARD)) {
        g2.drawLine((int) linkX, (int) linkY, (int) elbowX,
            (int) elbowY);
        g2.drawLine((int) anchorX, (int) anchorY, (int) elbowX,
            (int) elbowY);
        g2.drawLine((int) anchorX, (int) anchorY, (int) targetX,
            (int) targetY);
      }
    }

    final TextBox tb = record.getLabel();
    tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT);

  }

  /**
   * Draws the right labels.
   *
   * @param keys
   *            the keys.
   * @param g2
   *            the graphics device.
   * @param plotArea
   *            the plot area.
   * @param linkArea
   *            the link area.
   * @param maxLabelWidth
   *            the maximum label width.
   * @param state
   *            the state.
   */
  protected void drawRightLabels(final KeyedValues keys, final GC g2,
      final Rectangle plotArea, final Rectangle linkArea,
      final float maxLabelWidth, final PiePlotState state) {

    // draw the right labels...
    this.labelDistributor.clear();
    final double lGap = plotArea.width * this.labelGap;
    final double verticalLinkRadius = state.getLinkArea().height / 2.0;

    for (int i = 0; i < keys.getItemCount(); i++) {
      final String label = this.labelGenerator.generateSectionLabel(
          this.dataset, keys.getKey(i));

      if (label != null) {
        final TextBlock block = TextUtilities.createTextBlock(label,
            this.labelFont, this.labelPaint);
        final TextBox labelBox = new TextBox(block);
        labelBox.setBackgroundPaint(this.labelBackgroundPaint);
        labelBox.setOutlinePaint(this.labelOutlinePaint);
        labelBox.setOutlineStroke(this.labelOutlineStroke);
        labelBox.setShadowPaint(this.labelShadowPaint);
        labelBox.setInteriorGap(this.labelPadding);
        final double theta = Math.toRadians(keys.getValue(i)
            .doubleValue());
        final double baseY = state.getPieCenterY() - Math.sin(theta)
            * verticalLinkRadius;
        final double hh = labelBox.getHeight(g2);
        this.labelDistributor.addPieLabelRecord(new PieLabelRecord(keys
            .getKey(i), theta, baseY, labelBox, hh, lGap / 2.0
            + lGap / 2.0 * Math.cos(theta), 0.9 + this
            .getExplodePercent(keys.getKey(i))));
      }
    }
    final double hh = plotArea.height;
    final double gap = hh * this.getInteriorGap();
    this.labelDistributor.distributeLabels(RectangleUtil.getMinX(plotArea)
        + gap, hh - 2 * gap);
    for (int i = 0; i < this.labelDistributor.getItemCount(); i++) {
      this.drawRightLabel(g2, state,
          this.labelDistributor.getPieLabelRecord(i));
    }

  }

  /**
   * Draws the pie section labels in the simple form.
   *
   * @param g2
   *            the graphics device.
   * @param keys
   *            the section keys.
   * @param totalValue
   *            the total value for all sections in the pie.
   * @param plotArea
   *            the plot area.
   * @param pieArea
   *            the area containing the pie.
   * @param state
   *            the plot state.
   *
   * @since 1.0.7
   */
  protected void drawSimpleLabels(final GC g2, final List keys,
      final double totalValue, final Rectangle plotArea,
      final Rectangle pieArea, final PiePlotState state) {

    // Composite originalComposite = g2.getComposite();
    // g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    // 1.0f));

    final RectangleInsets labelInsets = new RectangleInsets(
        RectangleInsets.UnitType.RELATIVE, 0.18, 0.18, 0.18, 0.18);
    final Rectangle labelsArea = labelInsets.createInsetRectangle(pieArea);
    double runningTotal = 0.0;
    final Iterator iterator = keys.iterator();
    while (iterator.hasNext()) {
      final Comparable key = (Comparable) iterator.next();
      boolean include = true;
      double v = 0.0;
      final Number n = this.getDataset().getValue(key);
      if (n == null) {
        include = !this.getIgnoreNullValues();
      } else {
        v = n.doubleValue();
        include = this.getIgnoreZeroValues() ? v > 0.0 : v >= 0.0;
      }

      if (include) {
        runningTotal = runningTotal + v;
        // work out the mid angle (0 - 90 and 270 - 360) = right,
        // otherwise left
        final double mid = this.getStartAngle()
            + (this.getDirection().getFactor()
                * ((runningTotal - v / 2.0) * 360) / totalValue);

        final java.awt.geom.Arc2D arc = new java.awt.geom.Arc2D.Double(
            new Rectangle2D.Float(labelsArea.x, labelsArea.y,
                labelsArea.width, labelsArea.height),
            this.getStartAngle(), mid - this.getStartAngle(),
            java.awt.geom.Arc2D.OPEN);
        final int x = (int) arc.getEndPoint().getX();
        final int y = (int) arc.getEndPoint().getY();

        final PieSectionLabelGenerator labelGenerator = this
            .getLabelGenerator();
        if (labelGenerator == null) {
          continue;
        }
        final String label = labelGenerator.generateSectionLabel(
            this.dataset, key);
        if (label == null) {
          continue;
        }
        g2.setFont(this.labelFont);
        // FontMetrics fm = g2.getFontMetrics();
        final Rectangle bounds = TextUtilities.getTextBounds(label, g2);
        final Rectangle out = this.labelPadding
            .createOutsetRectangle(bounds);
        final Rectangle bg = new Rectangle(out.x, out.y, out.width,
            out.height);

        final int i = x - RectangleUtil.getCenterX(bounds);
        final int j = y - RectangleUtil.getCenterY(bounds);
        bg.x += (i);
        bg.y += (j);
        // if (this.labelShadowPaint != null) {
        // Shape shadow = ShapeUtilities.createTranslatedShape(bg,
        // this.shadowXOffset, this.shadowYOffset);
        // g2.setBackground(this.labelShadowPaint);
        // g2.fill(shadow);
        // }
        if (this.labelBackgroundPaint != null) {
          g2.setBackground(this.labelBackgroundPaint);
          g2.fillRectangle(bg);
        }
        if ((this.labelOutlinePaint != null)
            && (this.labelOutlineStroke != null)) {
          g2.setForeground(this.labelOutlinePaint);
          this.labelOutlineStroke.set(g2);
          g2.drawRectangle(bg);
        }

        g2.setBackground(this.labelPaint);
        g2.setFont(this.labelFont);
        TextUtilities.drawAlignedString(this.getLabelGenerator()
            .generateSectionLabel(this.getDataset(), key), g2, x,
            y, TextAnchor.CENTER);

      }
    }

    // g2.setComposite(originalComposite);

  }

  /**
   * Tests this plot for equality with an arbitrary object. Note that the
   * plot's dataset is NOT included in the test for equality.
   *
   * @param obj
   *            the object to test against (<code>null</code> permitted).
   *
   * @return <code>true</code> or <code>false</code>.
   */
  public boolean equals(final Object obj) {
    if (obj == this) {
      return true;
    }
    if (!(obj instanceof PiePlot)) {
      return false;
    }
    if (!super.equals(obj)) {
      return false;
    }
    final PiePlot that = (PiePlot) obj;
    if (this.pieIndex != that.pieIndex) {
      return false;
    }
    if (this.interiorGap != that.interiorGap) {
      return false;
    }
    if (this.circular != that.circular) {
      return false;
    }
    if (this.startAngle != that.startAngle) {
      return false;
    }
    if (this.direction != that.direction) {
      return false;
    }
    if (this.ignoreZeroValues != that.ignoreZeroValues) {
      return false;
    }
    if (this.ignoreNullValues != that.ignoreNullValues) {
      return false;
    }

    if (!ObjectUtilities.equal(this.sectionPaintMap, that.sectionPaintMap)) {
      return false;
    }

    if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) {
      return false;
    }

    if (!ObjectUtilities.equal(this.sectionOutlinePaintMap,
        that.sectionOutlinePaintMap)) {
      return false;
    }

    if (!ObjectUtilities.equal(this.sectionOutlineStroke,
        that.sectionOutlineStroke)) {
      return false;
    }

    if (!ObjectUtilities.equal(this.baseSectionOutlineStroke,
        that.baseSectionOutlineStroke)) {
      return false;
    }

    if (!(this.shadowXOffset == that.shadowXOffset)) {
      return false;
    }
    if (!(this.shadowYOffset == that.shadowYOffset)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.explodePercentages,
        that.explodePercentages)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.labelGenerator, that.labelGenerator)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
      return false;
    }

    if (!ObjectUtilities.equal(this.labelOutlineStroke,
        that.labelOutlineStroke)) {
      return false;
    }

    if (this.simpleLabels != that.simpleLabels) {
      return false;
    }
    if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) {
      return false;
    }
    if (!this.labelPadding.equals(that.labelPadding)) {
      return false;
    }
    if (!(this.maximumLabelWidth == that.maximumLabelWidth)) {
      return false;
    }
    if (!(this.labelGap == that.labelGap)) {
      return false;
    }
    if (!(this.labelLinkMargin == that.labelLinkMargin)) {
      return false;
    }
    if (this.labelLinksVisible != that.labelLinksVisible) {
      return false;
    }
    if (!this.labelLinkStyle.equals(that.labelLinkStyle)) {
      return false;
    }

    if (!ObjectUtilities.equal(this.labelLinkStroke, that.labelLinkStroke)) {
      return false;
    }
    if (!ObjectUtilities
        .equal(this.toolTipGenerator, that.toolTipGenerator)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
      return false;
    }
    if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) {
      return false;
    }

    if (!ObjectUtilities.equal(this.legendLabelGenerator,
        that.legendLabelGenerator)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.legendLabelToolTipGenerator,
        that.legendLabelToolTipGenerator)) {
      return false;
    }
    if (!ObjectUtilities.equal(this.legendLabelURLGenerator,
        that.legendLabelURLGenerator)) {
      return false;
    }
    // can't find any difference...
    return true;
  }

  /**
   * Returns a rectangle that can be used to create a pie section (taking into
   * account the amount by which the pie section is 'exploded').
   *
   * @param unexploded
   *            the area inside which the unexploded pie sections are drawn.
   * @param exploded
   *            the area inside which the exploded pie sections are drawn.
   * @param angle
   *            the start angle.
   * @param extent
   *            the extent of the arc.
   * @param explodePercent
   *            the amount by which the pie section is exploded.
   *
   * @return A rectangle that can be used to create a pie section.
   */
  protected Rectangle getArcBounds(final Rectangle unexploded,
      final Rectangle exploded, final double angle, final double extent,
      final double explodePercent) {

    if (explodePercent == 0.0) {
      return unexploded;
    } else {
      final Rectangle2D.Float er = new Rectangle2D.Float(unexploded.x,
          unexploded.y, unexploded.width, unexploded.height);
      final java.awt.geom.Arc2D arc1 = new java.awt.geom.Arc2D.Double(er,
          angle, extent / 2, java.awt.geom.Arc2D.OPEN);
      final java.awt.geom.Point2D point1 = arc1.getEndPoint();
      final java.awt.geom.Arc2D.Double arc2 = new java.awt.geom.Arc2D.Double(
          er, angle, extent / 2, java.awt.geom.Arc2D.OPEN);
      final java.awt.geom.Point2D point2 = arc2.getEndPoint();
      final double deltaX = (point1.getX() - point2.getX())
          * explodePercent;
      final double deltaY = (point1.getY() - point2.getY())
          * explodePercent;
      return new Rectangle((int) (unexploded.x - deltaX),
          (int) (unexploded.y - deltaY), unexploded.width,
          unexploded.height);
    }
  }

  /**
   * Returns the base section Color. This is used when no other Color is
   * available.
   *
   * @return The Color (never <code>null</code>).
   *
   * @see #setBaseSectionOutlinePaint(Color)
   */
  public Color getBaseSectionOutlinePaint() {
    return this.baseSectionOutlinePaint;
  }

  /**
   * Returns the base section stroke. This is used when no other stroke is
   * available.
   *
   * @return The stroke (never <code>null</code>).
   *
   * @see #setBaseSectionOutlineStroke(Stroke)
   */
  public Stroke getBaseSectionOutlineStroke() {
    return this.baseSectionOutlineStroke;
  }

  /**
   * Returns the base section Color. This is used when no other Color is
   * defined, which is rare. The default value is <code>Color.gray</code>.
   *
   * @return The Color (never <code>null</code>).
   *
   * @see #setBaseSectionPaint(Color)
   */
  public Color getBaseSectionPaint() {
    return this.baseSectionPaint;
  }

  /**
   * Returns the dataset.
   *
   * @return The dataset (possibly <code>null</code>).
   *
   * @see #setDataset(PieDataset)
   */
  public PieDataset getDataset() {
    return this.dataset;
  }

  /**
   * Returns the direction in which the pie sections are drawn (clockwise or
   * anti-clockwise).
   *
   * @return The direction (never <code>null</code>).
   *
   * @see #setDirection(Rotation)
   */
  public Rotation getDirection() {
    return this.direction;
  }

  // // SECTION Color ////////////////////////////////////////////////////////

  /**
   * Returns the amount that the section with the specified key should be
   * exploded.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   *
   * @return The amount that the section with the specified key should be
   *         exploded.
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @since 1.0.3
   *
   * @see #setExplodePercent(Comparable, double)
   */
  public double getExplodePercent(final Comparable key) {
    double result = 0.0;
    if (this.explodePercentages != null) {
      final Number percent = (Number) this.explodePercentages.get(key);
      if (percent != null) {
        result = percent.doubleValue();
      }
    }
    return result;
  }

  /**
   * Returns the amount that a section should be 'exploded'.
   *
   * @param section
   *            the section number.
   *
   * @return The amount that a section should be 'exploded'.
   *
   * @deprecated Use {@link #getExplodePercent(Comparable)} instead.
   */
  public double getExplodePercent(final int section) {
    final Comparable key = this.getSectionKey(section);
    return this.getExplodePercent(key);
  }

  /**
   * Returns the flag that controls whether <code>null</code> values in the
   * dataset are ignored.
   *
   * @return A boolean.
   *
   * @see #setIgnoreNullValues(boolean)
   */
  public boolean getIgnoreNullValues() {
    return this.ignoreNullValues;
  }

  /**
   * Returns the flag that controls whether zero values in the dataset are
   * ignored.
   *
   * @return A boolean.
   *
   * @see #setIgnoreZeroValues(boolean)
   */
  public boolean getIgnoreZeroValues() {
    return this.ignoreZeroValues;
  }

  /**
   * Returns the interior gap, measured as a percentage of the available
   * drawing space.
   *
   * @return The gap (as a percentage of the available drawing space).
   *
   * @see #setInteriorGap(double)
   */
  public double getInteriorGap() {
    return this.interiorGap;
  }

  /**
   * Returns the section label background Color.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setLabelBackgroundPaint(Color)
   */
  public Color getLabelBackgroundPaint() {
    return this.labelBackgroundPaint;
  }

  /**
   * Returns the object responsible for the vertical layout of the pie section
   * labels.
   *
   * @return The label distributor (never <code>null</code>).
   *
   * @since 1.0.6
   */
  public AbstractPieLabelDistributor getLabelDistributor() {
    return this.labelDistributor;
  }

  /**
   * Returns the section label font.
   *
   * @return The font (never <code>null</code>).
   *
   * @see #setLabelFont(Font)
   */
  public Font getLabelFont() {
    return this.labelFont;
  }

  /**
   * Returns the gap between the edge of the pie and the labels, expressed as
   * a percentage of the plot width.
   *
   * @return The gap (a percentage, where 0.05 = five percent).
   *
   * @see #setLabelGap(double)
   */
  public double getLabelGap() {
    return this.labelGap;
  }

  // // SECTION OUTLINE Color ////////////////////////////////////////////////

  /**
   * Returns the section label generator.
   *
   * @return The generator (possibly <code>null</code>).
   *
   * @see #setLabelGenerator(PieSectionLabelGenerator)
   */
  public PieSectionLabelGenerator getLabelGenerator() {
    return this.labelGenerator;
  }

  /**
   * Returns the margin (expressed as a percentage of the width or height)
   * between the edge of the pie and the link point.
   *
   * @return The link margin (as a percentage, where 0.05 is five percent).
   *
   * @see #setLabelLinkMargin(double)
   */
  public double getLabelLinkMargin() {
    return this.labelLinkMargin;
  }

  /**
   * Returns the Color used for the lines that connect pie sections to their
   * corresponding labels.
   *
   * @return The Color (never <code>null</code>).
   *
   * @see #setLabelLinkPaint(Color)
   */
  public Color getLabelLinkPaint() {
    return this.labelLinkPaint;
  }

  /**
   * Returns the stroke used for the label linking lines.
   *
   * @return The stroke.
   *
   * @see #setLabelLinkStroke(Stroke)
   */
  public Stroke getLabelLinkStroke() {
    return this.labelLinkStroke;
  }

  /**
   * Returns the label link style.
   *
   * @return The label link style (never <code>null</code>).
   *
   * @see #setLabelLinkStyle(PieLabelLinkStyle)
   *
   * @since 1.0.10
   */
  public PieLabelLinkStyle getLabelLinkStyle() {
    return this.labelLinkStyle;
  }

  /**
   * Returns the flag that controls whether or not label linking lines are
   * visible.
   *
   * @return A boolean.
   *
   * @see #setLabelLinksVisible(boolean)
   */
  public boolean getLabelLinksVisible() {
    return this.labelLinksVisible;
  }

  /**
   * Returns the section label outline Color.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setLabelOutlinePaint(Color)
   */
  public Color getLabelOutlinePaint() {
    return this.labelOutlinePaint;
  }

  /**
   * Returns the label padding.
   *
   * @return The label padding (never <code>null</code>).
   *
   * @since 1.0.7
   *
   * @see #setLabelPadding(RectangleInsets)
   */
  public RectangleInsets getLabelPadding() {
    return this.labelPadding;
  }

  /**
   * Returns the section label Color.
   *
   * @return The Color (never <code>null</code>).
   *
   * @see #setLabelPaint(Color)
   */
  public Color getLabelPaint() {
    return this.labelPaint;
  }

  /**
   * Returns the section label shadow Color.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setLabelShadowPaint(Color)
   */
  public Color getLabelShadowPaint() {
    return this.labelShadowPaint;
  }

  // // SECTION OUTLINE STROKE ///////////////////////////////////////////////

  /**
   * Returns a collection of legend items for the pie chart.
   *
   * @return The legend items (never <code>null</code>).
   */
  public LegendItemCollection getLegendItems() {

    final LegendItemCollection result = new LegendItemCollection();
    if (this.dataset == null) {
      return result;
    }
    final List keys = this.dataset.getKeys();
    int section = 0;
    this.getLegendItemShape();
    final Rectangle shape = new Rectangle(0, 0, 8, 8);
    final Iterator iterator = keys.iterator();
    while (iterator.hasNext()) {
      final Comparable key = (Comparable) iterator.next();
      final Number n = this.dataset.getValue(key);
      boolean include = true;
      if (n == null) {
        include = !this.ignoreNullValues;
      } else {
        final double v = n.doubleValue();
        if (v == 0.0) {
          include = !this.ignoreZeroValues;
        } else {
          include = v > 0.0;
        }
      }
      if (include) {
        final String label = this.legendLabelGenerator
            .generateSectionLabel(this.dataset, key);
        if (label != null) {
          final String description = label;
          String toolTipText = null;
          if (this.legendLabelToolTipGenerator != null) {
            toolTipText = this.legendLabelToolTipGenerator
                .generateSectionLabel(this.dataset, key);
          }
          String urlText = null;
          if (this.legendLabelURLGenerator != null) {
            urlText = this.legendLabelURLGenerator.generateURL(
                this.dataset, key, this.pieIndex);
          }
          final Color Color = this.lookupSectionPaint(key, true);
          final Color outlinePaint = this
              .lookupSectionOutlinePaint(key);
          final Stroke outlineStroke = this
              .lookupSectionOutlineStroke(key);
          final LegendItem item = new LegendItem(label, description,
              toolTipText, urlText, true, shape, true, Color,
              true, outlinePaint,
              outlineStroke,
              false, // line
              // not
              // visible
              new Rectangle(0, 0, 0, 0), new Stroke(1),
              StaticColorChecker.dublicateColor(SWT.COLOR_BLACK));
          // Display
          // .getDefault().getSystemColor(
          // SWT.COLOR_BLACK));
          item.setDataset(this.getDataset());
          result.add(item);
        }
        section++;
      } else {
        section++;
      }
    }
    return result;
  }

  /**
   * Returns the shape used for legend items.
   *
   * @return The shape (never <code>null</code>).
   *
   * @see #setLegendItemShape(Shape)
   */
  public Region getLegendItemShape() {
    return this.legendItemShape;
  }

  /**
   * Returns the legend label generator.
   *
   * @return The legend label generator (never <code>null</code>).
   *
   * @see #setLegendLabelGenerator(PieSectionLabelGenerator)
   */
  public PieSectionLabelGenerator getLegendLabelGenerator() {
    return this.legendLabelGenerator;
  }

  /**
   * Returns the legend label tool tip generator.
   *
   * @return The legend label tool tip generator (possibly <code>null</code>).
   *
   * @see #setLegendLabelToolTipGenerator(PieSectionLabelGenerator)
   */
  public PieSectionLabelGenerator getLegendLabelToolTipGenerator() {
    return this.legendLabelToolTipGenerator;
  }

  /**
   * Returns the legend label URL generator.
   *
   * @return The legend label URL generator (possibly <code>null</code>).
   *
   * @see #setLegendLabelURLGenerator(PieURLGenerator)
   *
   * @since 1.0.4
   */
  public PieURLGenerator getLegendLabelURLGenerator() {
    return this.legendLabelURLGenerator;
  }

  /**
   * Returns the maximum explode percent.
   *
   * @return The percent.
   */
  public double getMaximumExplodePercent() {
    if (this.dataset == null) {
      return 0.0;
    }
    double result = 0.0;
    final Iterator iterator = this.dataset.getKeys().iterator();
    while (iterator.hasNext()) {
      final Comparable key = (Comparable) iterator.next();
      final Number explode = (Number) this.explodePercentages.get(key);
      if (explode != null) {
        result = Math.max(result, explode.doubleValue());
      }
    }
    return result;
  }

  /**
   * Returns the maximum label width as a percentage of the plot width.
   *
   * @return The width (a percentage, where 0.20 = 20 percent).
   *
   * @see #setMaximumLabelWidth(double)
   */
  public double getMaximumLabelWidth() {
    return this.maximumLabelWidth;
  }

  /**
   * Returns the minimum arc angle that will be drawn. Pie sections for an
   * angle smaller than this are not drawn, to avoid a JDK bug.
   *
   * @return The minimum angle.
   *
   * @see #setMinimumArcAngleToDraw(double)
   */
  public double getMinimumArcAngleToDraw() {
    return this.minimumArcAngleToDraw;
  }

  /**
   * Returns the pie index (this is used by the {@link MultiplePiePlot} class
   * to track subplots).
   *
   * @return The pie index.
   *
   * @see #setPieIndex(int)
   */
  public int getPieIndex() {
    return this.pieIndex;
  }

  /**
   * Returns a short string describing the type of plot.
   *
   * @return The plot type.
   */
  public String getPlotType() {
    return localizationResources.getString("Pie_Plot");
  }

  /**
   * Returns a key for the specified section. If there is no such section in
   * the dataset, we generate a key. This is to provide some backward
   * compatibility for the (now deprecated) methods that get/set attributes
   * based on section indices. The preferred way of doing this now is to link
   * the attributes directly to the section key (there are new methods for
   * this, starting from version 1.0.3).
   *
   * @param section
   *            the section index.
   *
   * @return The key.
   *
   * @since 1.0.3
   */
  protected Comparable getSectionKey(final int section) {
    Comparable key = null;
    if (this.dataset != null) {
      if ((section >= 0) && (section < this.dataset.getItemCount())) {
        key = this.dataset.getKey(section);
      }
    }
    if (key == null) {
      key = new Integer(section);
    }
    return key;
  }

  /**
   * Returns the outline Color for ALL sections in the plot.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setSectionOutlinePaint(Color)
   *
   * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} and
   *             {@link #getBaseSectionOutlinePaint()}. Deprecated as of
   *             version 1.0.6.
   */
  public Color getSectionOutlinePaint() {
    return this.sectionOutlinePaint;
  }

  /**
   * Returns the outline Color associated with the specified key, or
   * <code>null</code> if there is no Color associated with the key.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   *
   * @return The Color associated with the specified key, or <code>null</code>
   *         .
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #setSectionOutlinePaint(Comparable, Color)
   *
   * @since 1.0.3
   */
  public Color getSectionOutlinePaint(final Comparable key) {
    // null argument check delegated...
    return (Color) this.sectionOutlinePaintMap.get(key);
  }

  /**
   * Returns the Color for the specified section.
   *
   * @param section
   *            the section index (zero-based).
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} instead.
   */
  public Color getSectionOutlinePaint(final int section) {
    final Comparable key = this.getSectionKey(section);
    return this.getSectionOutlinePaint(key);
  }

  /**
   * Returns the outline stroke for ALL sections in the plot.
   *
   * @return The stroke (possibly <code>null</code>).
   *
   * @see #setSectionOutlineStroke(Stroke)
   *
   * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} and
   *             {@link #getBaseSectionOutlineStroke()}. Deprecated as of
   *             version 1.0.6.
   */
  public Stroke getSectionOutlineStroke() {
    return this.sectionOutlineStroke;
  }

  /**
   * Returns the outline stroke associated with the specified key, or
   * <code>null</code> if there is no stroke associated with the key.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   *
   * @return The stroke associated with the specified key, or
   *         <code>null</code>.
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #setSectionOutlineStroke(Comparable, Stroke)
   *
   * @since 1.0.3
   */
  public Stroke getSectionOutlineStroke(final Comparable key) {
    // null argument check delegated...
    return (Stroke) this.sectionOutlineStrokeMap.get(key);
  }

  /**
   * Returns the flag that controls whether or not the outline is drawn for
   * each pie section.
   *
   * @return The flag that controls whether or not the outline is drawn for
   *         each pie section.
   *
   * @see #setSectionOutlinesVisible(boolean)
   */
  public boolean getSectionOutlinesVisible() {
    return this.sectionOutlinesVisible;
  }

  /**
   * Returns the Color for ALL sections in the plot.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setSectionPaint(Color)
   *
   * @deprecated Use {@link #getSectionPaint(Comparable)} and
   *             {@link #getBaseSectionPaint()}. Deprecated as of version
   *             1.0.6.
   */
  public Color getSectionPaint() {
    return this.sectionPaint;
  }

  /**
   * Returns the Color associated with the specified key, or <code>null</code>
   * if there is no Color associated with the key.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   *
   * @return The Color associated with the specified key, or <code>null</code>
   *         .
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #setSectionPaint(Comparable, Color)
   *
   * @since 1.0.3
   */
  public Color getSectionPaint(final Comparable key) {
    // null argument check delegated...
    return (Color) this.sectionPaintMap.get(key);
  }

  /**
   * Returns the Color for the specified section.
   *
   * @param section
   *            the section index (zero-based).
   *
   * @return The Color (never <code>null</code>).
   *
   * @deprecated Use {@link #getSectionPaint(Comparable)} instead.
   */
  public Color getSectionPaint(final int section) {
    final Comparable key = this.getSectionKey(section);
    return this.getSectionPaint(key);
  }

  /**
   * Returns the shadow Color.
   *
   * @return The Color (possibly <code>null</code>).
   *
   * @see #setShadowPaint(Color)
   */
  public Color getShadowPaint() {
    return this.shadowPaint;
  }

  /**
   * Returns the x-offset for the shadow effect.
   *
   * @return The offset (in Java2D units).
   *
   * @see #setShadowXOffset(double)
   */
  public double getShadowXOffset() {
    return this.shadowXOffset;
  }

  /**
   * Returns the y-offset for the shadow effect.
   *
   * @return The offset (in Java2D units).
   *
   * @see #setShadowYOffset(double)
   */
  public double getShadowYOffset() {
    return this.shadowYOffset;
  }

  /**
   * Returns the offset used for the simple labels, if they are displayed.
   *
   * @return The offset (never <code>null</code>).
   *
   * @since 1.0.7
   *
   * @see #setSimpleLabelOffset(RectangleInsets)
   */
  public RectangleInsets getSimpleLabelOffset() {
    return this.simpleLabelOffset;
  }

  /**
   * Returns the flag that controls whether simple or extended labels are
   * displayed on the plot.
   *
   * @return A boolean.
   *
   * @since 1.0.7
   */
  public boolean getSimpleLabels() {
    return this.simpleLabels;
  }

  /**
   * Returns the start angle for the first pie section. This is measured in
   * degrees starting from 3 o'clock and measuring anti-clockwise.
   *
   * @return The start angle.
   *
   * @see #setStartAngle(double)
   */
  public double getStartAngle() {
    return this.startAngle;
  }

  /**
   * Returns the tool tip generator, an object that is responsible for
   * generating the text items used for tool tips by the plot. If the
   * generator is <code>null</code>, no tool tips will be created.
   *
   * @return The generator (possibly <code>null</code>).
   *
   * @see #setToolTipGenerator(PieToolTipGenerator)
   */
  public PieToolTipGenerator getToolTipGenerator() {
    return this.toolTipGenerator;
  }

  /**
   * Returns the URL generator.
   *
   * @return The generator (possibly <code>null</code>).
   *
   * @see #setURLGenerator(PieURLGenerator)
   */
  public PieURLGenerator getURLGenerator() {
    return this.urlGenerator;
  }

  /**
   * Initialises the drawing procedure. This method will be called before the
   * first item is rendered, giving the plot an opportunity to initialise any
   * state information it wants to maintain.
   *
   * @param g2
   *            the graphics device.
   * @param plotArea
   *            the plot area (<code>null</code> not permitted).
   * @param plot
   *            the plot.
   * @param index
   *            the secondary index (<code>null</code> for primary renderer).
   * @param info
   *            collects chart rendering information for return to caller.
   *
   * @return A state object (maintains state information relevant to one chart
   *         drawing).
   */
  public PiePlotState initialise(final GC g2, final Rectangle plotArea,
      final PiePlot plot, final Integer index,
      final PlotRenderingInfo info) {

    final PiePlotState state = new PiePlotState(info);
    state.setPassesRequired(2);
    if (this.dataset != null) {
      state.setTotal(DatasetUtilities.calculatePieDatasetTotal(plot
          .getDataset()));
    }
    state.setLatestAngle(plot.getStartAngle());
    return state;

  }

  /**
   * Returns a flag indicating whether the pie chart is circular, or stretched
   * into an elliptical shape.
   *
   * @return A flag indicating whether the pie chart is circular.
   *
   * @see #setCircular(boolean)
   */
  public boolean isCircular() {
    return this.circular;
  }

  /**
   * Returns the outline Color for the specified section. This is equivalent
   * to <code>lookupSectionPaint(section, false)</code>.
   *
   * @param key
   *            the section key.
   *
   * @return The Color for the specified section.
   *
   * @since 1.0.3
   *
   * @see #lookupSectionOutlinePaint(Comparable, boolean)
   */
  protected Color lookupSectionOutlinePaint(final Comparable key) {
    final Color lookupSectionOutlinePaint = this.lookupSectionOutlinePaint(
        key, false);
    if (lookupSectionOutlinePaint == null) {
      return DEFAULT_LABEL_OUTLINE_PAINT;
    }
    return lookupSectionOutlinePaint;
  }

  /**
   * Returns the outline Color for the specified section. The lookup involves
   * these steps:
   * <ul>
   * <li>if {@link #getSectionOutlinePaint()} is non-<code>null</code>, return
   * it;</li>
   * <li>otherwise, if {@link #getSectionOutlinePaint(int)} is non-
   * <code>null</code> return it;</li>
   * <li>if {@link #getSectionOutlinePaint(int)} is <code>null</code> but
   * <code>
   * autoPopulate</code> is <code>true</code>, attempt to fetch a new outline
   * Color from the drawing supplier ({@link #getDrawingSupplier()});
   * <li>if all else fails, return {@link #getBaseSectionOutlinePaint()}.
   * </ul>
   *
   * @param key
   *            the section key.
   * @param autoPopulate
   *            a flag that controls whether the drawing supplier is used to
   *            auto-populate the section outline Color settings.
   *
   * @return The Color.
   *
   * @since 1.0.3
   */
  protected Color lookupSectionOutlinePaint(final Comparable key,
      final boolean autoPopulate) {

    // is there an override?
    Color result = this.getSectionOutlinePaint();
    if (result != null) {
      return result;
    }

    // if not, check if there is a Color defined for the specified key
    result = (Color) this.sectionOutlinePaintMap.get(key);
    if (result != null) {
      return result;
    }

    // nothing defined - do we autoPopulate?
    if (autoPopulate) {
      final DrawingSupplier ds = this.getDrawingSupplier();
      if (ds != null) {
        result = ds.getNextOutlinePaint();
        this.sectionOutlinePaintMap.put(key, result);
      } else {
        result = this.baseSectionOutlinePaint;
      }
    } else {
      result = this.baseSectionOutlinePaint;
    }
    return result;
  }

  /**
   * Returns the outline stroke for the specified section. This is equivalent
   * to <code>lookupSectionOutlineStroke(section, false)</code>.
   *
   * @param key
   *            the section key.
   *
   * @return The stroke for the specified section.
   *
   * @since 1.0.3
   *
   * @see #lookupSectionOutlineStroke(Comparable, boolean)
   */
  protected Stroke lookupSectionOutlineStroke(final Comparable key) {
    final Stroke lookupSectionOutlineStroke = this
        .lookupSectionOutlineStroke(key, false);
    if (lookupSectionOutlineStroke == null) {
      return new Stroke(1);
    }
    return lookupSectionOutlineStroke;
  }

  /**
   * Returns the outline stroke for the specified section. The lookup involves
   * these steps:
   * <ul>
   * <li>if {@link #getSectionOutlineStroke()} is non-<code>null</code>,
   * return it;</li>
   * <li>otherwise, if {@link #getSectionOutlineStroke(int)} is non-
   * <code>null</code> return it;</li>
   * <li>if {@link #getSectionOutlineStroke(int)} is <code>null</code> but
   * <code>
   * autoPopulate</code> is <code>true</code>, attempt to fetch a new outline
   * stroke from the drawing supplier ({@link #getDrawingSupplier()});
   * <li>if all else fails, return {@link #getBaseSectionOutlineStroke()}.
   * </ul>
   *
   * @param key
   *            the section key.
   * @param autoPopulate
   *            a flag that controls whether the drawing supplier is used to
   *            auto-populate the section outline stroke settings.
   *
   * @return The stroke.
   *
   * @since 1.0.3
   */
  protected Stroke lookupSectionOutlineStroke(final Comparable key,
      final boolean autoPopulate) {

    // is there an override?
    Stroke result = this.getSectionOutlineStroke();
    if (result != null) {
      return result;
    }

    // if not, check if there is a stroke defined for the specified key
    result = (Stroke) this.sectionOutlineStrokeMap.get(key);
    if (result != null) {
      return result;
    }

    // nothing defined - do we autoPopulate?
    if (autoPopulate) {
      final DrawingSupplier ds = this.getDrawingSupplier();
      if (ds != null) {
        result = ds.getNextOutlineStroke();
        this.sectionOutlineStrokeMap.put(key, result);
      } else {
        result = this.baseSectionOutlineStroke;
      }
    } else {
      result = this.baseSectionOutlineStroke;
    }
    return result;
  }

  /**
   * Returns the Color for the specified section. This is equivalent to
   * <code>lookupSectionPaint(section, false)</code>.
   *
   * @param key
   *            the section key.
   *
   * @return The Color for the specified section.
   *
   * @since 1.0.3
   *
   * @see #lookupSectionPaint(Comparable, boolean)
   */
  protected Color lookupSectionPaint(final Comparable key) {
    return this.lookupSectionPaint(key, false);
  }

  /**
   * Returns the Color for the specified section. The lookup involves these
   * steps:
   * <ul>
   * <li>if {@link #getSectionPaint()} is non-<code>null</code>, return it;</li>
   * <li>if {@link #getSectionPaint(int)} is non-<code>null</code> return it;</li>
   * <li>if {@link #getSectionPaint(int)} is <code>null</code> but
   * <code>autoPopulate</code> is <code>true</code>, attempt to fetch a new
   * Color from the drawing supplier ({@link #getDrawingSupplier()});
   * <li>
   * if all else fails, return {@link #getBaseSectionPaint()}.
   * </ul>
   *
   * @param key
   *            the section key.
   * @param autoPopulate
   *            a flag that controls whether the drawing supplier is used to
   *            auto-populate the section Color settings.
   *
   * @return The Color.
   *
   * @since 1.0.3
   */
  public Color lookupSectionPaint(final Comparable key,
      final boolean autoPopulate) {

    // is there an override?
    Color result = this.getSectionPaint();
    if (result != null) {
      return result;
    }

    // if not, check if there is a Color defined for the specified key
    result = (Color) this.sectionPaintMap.get(key);
    if (result != null) {
      return result;
    }

    // nothing defined - do we autoPopulate?
    if (autoPopulate) {
      final DrawingSupplier ds = this.getDrawingSupplier();
      if (ds != null) {
        result = ds.getNextPaint();
        this.sectionPaintMap.put(key, result);
      } else {
        result = this.baseSectionPaint;
      }
    } else {
      result = this.baseSectionPaint;
    }
    return result;
  }

  /**
   * Provides serialization support.
   *
   * @param stream
   *            the input stream.
   *
   * @throws IOException
   *             if there is an I/O error.
   * @throws ClassNotFoundException
   *             if there is a classpath problem.
   */
  private void readObject(final ObjectInputStream stream) throws IOException,
      ClassNotFoundException {
    stream.defaultReadObject();
    // //this.sectionPaint = SerialUtilities.readPaint(stream);
    // //this.baseSectionPaint = SerialUtilities.readPaint(stream);
    // //this.sectionOutlinePaint = SerialUtilities.readPaint(stream);
    // this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream);
    // this.sectionOutlineStroke = SerialUtilities.readStroke(stream);
    // this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream);
    // this.shadowPaint = SerialUtilities.readPaint(stream);
    // this.labelPaint = SerialUtilities.readPaint(stream);
    // this.labelBackgroundPaint = SerialUtilities.readPaint(stream);
    // this.labelOutlinePaint = SerialUtilities.readPaint(stream);
    // this.labelOutlineStroke = SerialUtilities.readStroke(stream);
    // this.labelShadowPaint = SerialUtilities.readPaint(stream);
    // this.labelLinkPaint = SerialUtilities.readPaint(stream);
    // this.labelLinkStroke = SerialUtilities.readStroke(stream);
    // this.legendItemShape = SerialUtilities.readShape(stream);
  }

  /**
   * Sets the base section Color.
   *
   * @param Color
   *            the Color (<code>null</code> not permitted).
   *
   * @see #getBaseSectionOutlinePaint()
   */
  public void setBaseSectionOutlinePaint(final Color Color) {
    if (Color == null) {
      throw new IllegalArgumentException("Null 'Color' argument.");
    }
    this.baseSectionOutlinePaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the base section stroke.
   *
   * @param stroke
   *            the stroke (<code>null</code> not permitted).
   *
   * @see #getBaseSectionOutlineStroke()
   */
  public void setBaseSectionOutlineStroke(final Stroke stroke) {
    if (stroke == null) {
      throw new IllegalArgumentException("Null 'stroke' argument.");
    }
    this.baseSectionOutlineStroke = stroke;
    this.fireChangeEvent();
  }

  /**
   * Sets the base section Color and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> not permitted).
   *
   * @see #getBaseSectionPaint()
   */
  public void setBaseSectionPaint(final Color Color) {
    if (Color == null) {
      throw new IllegalArgumentException("Null 'Color' argument.");
    }
    this.baseSectionPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * A flag indicating whether the pie chart is circular, or stretched into an
   * elliptical shape.
   *
   * @param flag
   *            the new value.
   *
   * @see #isCircular()
   */
  public void setCircular(final boolean flag) {
    this.setCircular(flag, true);
  }

  // /**
  // * Returns the section label outline stroke.
  // *
  // * @return The stroke (possibly <code>null</code>).
  // *
  // * @see #setLabelOutlineStroke(Stroke)
  // */
  // public Stroke getLabelOutlineStroke() {
  // return this.labelOutlineStroke;
  // }

  /**
   * Sets the circular attribute and, if requested, sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param circular
   *            the new value of the flag.
   * @param notify
   *            notify listeners?
   *
   * @see #isCircular()
   */
  public void setCircular(final boolean circular, final boolean notify) {
    this.circular = circular;
    if (notify) {
      this.fireChangeEvent();
    }
  }

  /**
   * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'.
   *
   * @param dataset
   *            the dataset (<code>null</code> permitted).
   *
   * @see #getDataset()
   */
  public void setDataset(final PieDataset dataset) {
    // if there is an existing dataset, remove the plot from the list of
    // change listeners...
    final PieDataset existing = this.dataset;
    if (existing != null) {
      existing.removeChangeListener(this);
    }

    // set the new dataset, and register the chart as a change listener...
    this.dataset = dataset;
    if (dataset != null) {
      this.setDatasetGroup(dataset.getGroup());
      dataset.addChangeListener(this);
    }

    // send a dataset change event to self...
    final DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
    this.datasetChanged(event);
  }

  /**
   * Sets the direction in which the pie sections are drawn and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param direction
   *            the direction (<code>null</code> not permitted).
   *
   * @see #getDirection()
   */
  public void setDirection(final Rotation direction) {
    if (direction == null) {
      throw new IllegalArgumentException("Null 'direction' argument.");
    }
    this.direction = direction;
    this.fireChangeEvent();

  }

  /**
   * Sets the amount that a pie section should be exploded and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param key
   *            the section key (<code>null</code> not permitted).
   * @param percent
   *            the explode percentage (0.30 = 30 percent).
   *
   * @since 1.0.3
   *
   * @see #getExplodePercent(Comparable)
   */
  public void setExplodePercent(final Comparable key, final double percent) {
    if (key == null) {
      throw new IllegalArgumentException("Null 'key' argument.");
    }
    if (this.explodePercentages == null) {
      this.explodePercentages = new TreeMap();
    }
    this.explodePercentages.put(key, new Double(percent));
    this.fireChangeEvent();
  }

  /**
   * Sets the amount that a pie section should be exploded and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param section
   *            the section index.
   * @param percent
   *            the explode percentage (0.30 = 30 percent).
   *
   * @deprecated Use {@link #setExplodePercent(Comparable, double)} instead.
   */
  public void setExplodePercent(final int section, final double percent) {
    final Comparable key = this.getSectionKey(section);
    this.setExplodePercent(key, percent);
  }

  /**
   * Sets a flag that controls whether <code>null</code> values are ignored,
   * and sends a {@link PlotChangeEvent} to all registered listeners. At
   * present, this only affects whether or not the key is presented in the
   * legend.
   *
   * @param flag
   *            the flag.
   *
   * @see #getIgnoreNullValues()
   * @see #setIgnoreZeroValues(boolean)
   */
  public void setIgnoreNullValues(final boolean flag) {
    this.ignoreNullValues = flag;
    this.fireChangeEvent();
  }

  /**
   * Sets a flag that controls whether zero values are ignored, and sends a
   * {@link PlotChangeEvent} to all registered listeners. This only affects
   * whether or not a label appears for the non-visible pie section.
   *
   * @param flag
   *            the flag.
   *
   * @see #getIgnoreZeroValues()
   * @see #setIgnoreNullValues(boolean)
   */
  public void setIgnoreZeroValues(final boolean flag) {
    this.ignoreZeroValues = flag;
    this.fireChangeEvent();
  }

  /**
   * Sets the interior gap and sends a {@link PlotChangeEvent} to all
   * registered listeners. This controls the space between the edges of the
   * pie plot and the plot area itself (the region where the section labels
   * appear).
   *
   * @param percent
   *            the gap (as a percentage of the available drawing space).
   *
   * @see #getInteriorGap()
   */
  public void setInteriorGap(final double percent) {

    if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) {
      throw new IllegalArgumentException("Invalid 'percent' (" + percent
          + ") argument.");
    }

    if (this.interiorGap != percent) {
      this.interiorGap = percent;
      this.fireChangeEvent();
    }

  }

  /**
   * Sets the section label background Color and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getLabelBackgroundPaint()
   */
  public void setLabelBackgroundPaint(final Color Color) {
    this.labelBackgroundPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the label distributor and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param distributor
   *            the distributor (<code>null</code> not permitted).
   *
   * @since 1.0.6
   */
  public void setLabelDistributor(
      final AbstractPieLabelDistributor distributor) {
    if (distributor == null) {
      throw new IllegalArgumentException("Null 'distributor' argument.");
    }
    this.labelDistributor = distributor;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label font and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param font
   *            the font (<code>null</code> not permitted).
   *
   * @see #getLabelFont()
   */
  public void setLabelFont(final Font font) {
    if (font == null) {
      throw new IllegalArgumentException("Null 'font' argument.");
    }
    this.labelFont = font;
    this.fireChangeEvent();
  }

  /**
   * Sets the gap between the edge of the pie and the labels (expressed as a
   * percentage of the plot width) and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param gap
   *            the gap (a percentage, where 0.05 = five percent).
   *
   * @see #getLabelGap()
   */
  public void setLabelGap(final double gap) {
    this.labelGap = gap;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label generator and sends a {@link PlotChangeEvent} to
   * all registered listeners.
   *
   * @param generator
   *            the generator (<code>null</code> permitted).
   *
   * @see #getLabelGenerator()
   */
  public void setLabelGenerator(final PieSectionLabelGenerator generator) {
    this.labelGenerator = generator;
    this.fireChangeEvent();
  }

  /**
   * Sets the link margin and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param margin
   *            the margin.
   *
   * @see #getLabelLinkMargin()
   */
  public void setLabelLinkMargin(final double margin) {
    this.labelLinkMargin = margin;
    this.fireChangeEvent();
  }

  /**
   * Sets the Color used for the lines that connect pie sections to their
   * corresponding labels, and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> not permitted).
   *
   * @see #getLabelLinkPaint()
   */
  public void setLabelLinkPaint(final Color Color) {
    if (Color == null) {
      throw new IllegalArgumentException("Null 'Color' argument.");
    }
    this.labelLinkPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the link stroke and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param stroke
   *            the stroke.
   *
   * @see #getLabelLinkStroke()
   */
  public void setLabelLinkStroke(final Stroke stroke) {
    if (stroke == null) {
      throw new IllegalArgumentException("Null 'stroke' argument.");
    }
    this.labelLinkStroke = stroke;
    this.fireChangeEvent();
  }

  /**
   * Sets the label link style and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param style
   *            the new style (<code>null</code> not permitted).
   *
   * @see #getLabelLinkStyle()
   *
   * @since 1.0.10
   */
  public void setLabelLinkStyle(final PieLabelLinkStyle style) {
    if (style == null) {
      throw new IllegalArgumentException("Null 'style' argument.");
    }
    this.labelLinkStyle = style;
    this.fireChangeEvent();
  }

  /**
   * Sets the flag that controls whether or not label linking lines are
   * visible and sends a {@link PlotChangeEvent} to all registered listeners.
   * Please take care when hiding the linking lines - depending on the data
   * values, the labels can be displayed some distance away from the
   * corresponding pie section.
   *
   * @param visible
   *            the flag.
   *
   * @see #getLabelLinksVisible()
   */
  public void setLabelLinksVisible(final boolean visible) {
    this.labelLinksVisible = visible;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label outline Color and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getLabelOutlinePaint()
   */
  public void setLabelOutlinePaint(final Color Color) {
    this.labelOutlinePaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label outline stroke and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param stroke
   *            the stroke (<code>null</code> permitted).
   *
   * @see #getLabelOutlineStroke()
   */
  public void setLabelOutlineStroke(final Stroke stroke) {
    this.labelOutlineStroke = stroke;
    this.fireChangeEvent();
  }

  /**
   * Sets the padding between each label and its outline and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param padding
   *            the padding (<code>null</code> not permitted).
   *
   * @since 1.0.7
   *
   * @see #getLabelPadding()
   */
  public void setLabelPadding(final RectangleInsets padding) {
    if (padding == null) {
      throw new IllegalArgumentException("Null 'padding' argument.");
    }
    this.labelPadding = padding;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label Color and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> not permitted).
   *
   * @see #getLabelPaint()
   */
  public void setLabelPaint(final Color Color) {
    if (Color == null) {
      throw new IllegalArgumentException("Null 'Color' argument.");
    }
    this.labelPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the section label shadow Color and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getLabelShadowPaint()
   */
  public void setLabelShadowPaint(final Color Color) {
    this.labelShadowPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the shape used for legend items and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param shape
   *            the shape (<code>null</code> not permitted).
   *
   * @see #getLegendItemShape()
   */
  public void setLegendItemShape(final Region shape) {
    if (shape == null) {
      throw new IllegalArgumentException("Null 'shape' argument.");
    }
    this.legendItemShape = shape;
    this.fireChangeEvent();
  }

  /**
   * Sets the legend label generator and sends a {@link PlotChangeEvent} to
   * all registered listeners.
   *
   * @param generator
   *            the generator (<code>null</code> not permitted).
   *
   * @see #getLegendLabelGenerator()
   */
  public void setLegendLabelGenerator(final PieSectionLabelGenerator generator) {
    if (generator == null) {
      throw new IllegalArgumentException("Null 'generator' argument.");
    }
    this.legendLabelGenerator = generator;
    this.fireChangeEvent();
  }

  /**
   * Sets the legend label tool tip generator and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param generator
   *            the generator (<code>null</code> permitted).
   *
   * @see #getLegendLabelToolTipGenerator()
   */
  public void setLegendLabelToolTipGenerator(
      final PieSectionLabelGenerator generator) {
    this.legendLabelToolTipGenerator = generator;
    this.fireChangeEvent();
  }

  /**
   * Sets the legend label URL generator and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param generator
   *            the generator (<code>null</code> permitted).
   *
   * @see #getLegendLabelURLGenerator()
   *
   * @since 1.0.4
   */
  public void setLegendLabelURLGenerator(final PieURLGenerator generator) {
    this.legendLabelURLGenerator = generator;
    this.fireChangeEvent();
  }

  /**
   * Sets the maximum label width as a percentage of the plot width and sends
   * a {@link PlotChangeEvent} to all registered listeners.
   *
   * @param width
   *            the width (a percentage, where 0.20 = 20 percent).
   *
   * @see #getMaximumLabelWidth()
   */
  public void setMaximumLabelWidth(final double width) {
    this.maximumLabelWidth = width;
    this.fireChangeEvent();
  }

  /**
   * Sets the minimum arc angle that will be drawn. Pie sections for an angle
   * smaller than this are not drawn, to avoid a JDK bug. See this link for
   * details: <br>
   * <br>
   * <a href="http://www.jfree.org/phpBB2/viewtopic.php?t=2707">
   * http://www.jfree.org/phpBB2/viewtopic.php?t=2707</a> <br>
   * <br>
   * ...and this bug report in the Java Bug Parade: <br>
   * <br>
   * <a href=
   * "http://developer.java.sun.com/developer/bugParade/bugs/4836495.html">
   * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html</a>
   *
   * @param angle
   *            the minimum angle.
   *
   * @see #getMinimumArcAngleToDraw()
   */
  public void setMinimumArcAngleToDraw(final double angle) {
    this.minimumArcAngleToDraw = angle;
  }

  /**
   * Sets the pie index (this is used by the {@link MultiplePiePlot} class to
   * track subplots).
   *
   * @param index
   *            the index.
   *
   * @see #getPieIndex()
   */
  public void setPieIndex(final int index) {
    this.pieIndex = index;
  }

  /**
   * Sets the outline Color for ALL sections in the plot. If this is set to
   * </code>null</code>, then a list of paints is used instead (to allow
   * different colors to be used for each section).
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getSectionOutlinePaint()
   *
   * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Color)} and
   *             {@link #setBaseSectionOutlinePaint(Color)}. Deprecated as of
   *             version 1.0.6.
   */
  public void setSectionOutlinePaint(final Color Color) {
    this.sectionOutlinePaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the outline Color associated with the specified key, and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   * @param Color
   *            the Color.
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #getSectionOutlinePaint(Comparable)
   *
   * @since 1.0.3
   */
  public void setSectionOutlinePaint(final Comparable key, final Color Color) {
    // null argument check delegated...
    this.sectionOutlinePaintMap.put(key, Color);
    this.fireChangeEvent();
  }

  /**
   * Sets the Color used to fill a section of the pie and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param section
   *            the section index (zero-based).
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Color)}
   *             instead.
   */
  public void setSectionOutlinePaint(final int section, final Color Color) {
    final Comparable key = this.getSectionKey(section);
    this.setSectionOutlinePaint(key, Color);
  }

  /**
   * Sets the outline stroke associated with the specified key, and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   * @param stroke
   *            the stroke.
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #getSectionOutlineStroke(Comparable)
   *
   * @since 1.0.3
   */
  public void setSectionOutlineStroke(final Comparable key,
      final Stroke stroke) {
    // null argument check delegated...
    this.sectionOutlineStrokeMap.put(key, stroke);
    this.fireChangeEvent();
  }

  /**
   * Sets the outline stroke for ALL sections in the plot. If this is set to
   * </code>null</code>, then a list of paints is used instead (to allow
   * different colors to be used for each section).
   *
   * @param stroke
   *            the stroke (<code>null</code> permitted).
   *
   * @see #getSectionOutlineStroke()
   *
   * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} and
   *             {@link #setBaseSectionOutlineStroke(Stroke)}. Deprecated as
   *             of version 1.0.6.
   */
  public void setSectionOutlineStroke(final Stroke stroke) {
    this.sectionOutlineStroke = stroke;
    this.fireChangeEvent();
  }

  /**
   * Sets the flag that controls whether or not the outline is drawn for each
   * pie section, and sends a {@link PlotChangeEvent} to all registered
   * listeners.
   *
   * @param visible
   *            the flag.
   *
   * @see #getSectionOutlinesVisible()
   */
  public void setSectionOutlinesVisible(final boolean visible) {
    this.sectionOutlinesVisible = visible;
    this.fireChangeEvent();
  }

  /**
   * Sets the Color for ALL sections in the plot. If this is set to
   * </code>null</code>, then a list of paints is used instead (to allow
   * different colors to be used for each section).
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getSectionPaint()
   *
   * @deprecated Use {@link #setSectionPaint(Comparable, Color)} and
   *             {@link #setBaseSectionPaint(Color)}. Deprecated as of version
   *             1.0.6.
   */
  public void setSectionPaint(final Color Color) {
    this.sectionPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the Color associated with the specified key, and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param key
   *            the key (<code>null</code> not permitted).
   * @param Color
   *            the Color.
   *
   * @throws IllegalArgumentException
   *             if <code>key</code> is <code>null</code>.
   *
   * @see #getSectionPaint(Comparable)
   *
   * @since 1.0.3
   */
  public void setSectionPaint(final Comparable key, final Color Color) {
    // null argument check delegated...
    this.sectionPaintMap.put(key, Color);
    this.fireChangeEvent();
  }

  /**
   * Sets the Color used to fill a section of the pie and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param section
   *            the section index (zero-based).
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @deprecated Use {@link #setSectionPaint(Comparable, Color)} instead.
   */
  public void setSectionPaint(final int section, final Color Color) {
    final Comparable key = this.getSectionKey(section);
    this.setSectionPaint(key, Color);
  }

  /**
   * Sets the shadow Color and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param Color
   *            the Color (<code>null</code> permitted).
   *
   * @see #getShadowPaint()
   */
  public void setShadowPaint(final Color Color) {
    this.shadowPaint = Color;
    this.fireChangeEvent();
  }

  /**
   * Sets the x-offset for the shadow effect and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param offset
   *            the offset (in Java2D units).
   *
   * @see #getShadowXOffset()
   */
  public void setShadowXOffset(final double offset) {
    this.shadowXOffset = offset;
    this.fireChangeEvent();
  }

  /**
   * Sets the y-offset for the shadow effect and sends a
   * {@link PlotChangeEvent} to all registered listeners.
   *
   * @param offset
   *            the offset (in Java2D units).
   *
   * @see #getShadowYOffset()
   */
  public void setShadowYOffset(final double offset) {
    this.shadowYOffset = offset;
    this.fireChangeEvent();
  }

  // DEPRECATED METHODS...

  /**
   * Sets the offset for the simple labels and sends a {@link PlotChangeEvent}
   * to all registered listeners.
   *
   * @param offset
   *            the offset (<code>null</code> not permitted).
   *
   * @since 1.0.7
   *
   * @see #getSimpleLabelOffset()
   */
  public void setSimpleLabelOffset(final RectangleInsets offset) {
    if (offset == null) {
      throw new IllegalArgumentException("Null 'offset' argument.");
    }
    this.simpleLabelOffset = offset;
    this.fireChangeEvent();
  }

  /**
   * Sets the flag that controls whether simple or extended labels are
   * displayed on the plot, and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param simple
   *            the new flag value.
   *
   * @since 1.0.7
   */
  public void setSimpleLabels(final boolean simple) {
    this.simpleLabels = simple;
    this.fireChangeEvent();
  }

  /**
   * Sets the starting angle and sends a {@link PlotChangeEvent} to all
   * registered listeners. The initial default value is 90 degrees, which
   * corresponds to 12 o'clock. A value of zero corresponds to 3 o'clock...
   * this is the encoding used by Java's Arc2D class.
   *
   * @param angle
   *            the angle (in degrees).
   *
   * @see #getStartAngle()
   */
  public void setStartAngle(final double angle) {
    this.startAngle = angle;
    this.fireChangeEvent();
  }

  /**
   * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all
   * registered listeners. Set the generator to <code>null</code> if you don't
   * want any tool tips.
   *
   * @param generator
   *            the generator (<code>null</code> permitted).
   *
   * @see #getToolTipGenerator()
   */
  public void setToolTipGenerator(final PieToolTipGenerator generator) {
    this.toolTipGenerator = generator;
    this.fireChangeEvent();
  }

  // /**
  // * Returns the stroke for the specified section.
  // *
  // * @param section the section index (zero-based).
  // *
  // * @return The stroke (possibly <code>null</code>).
  // *
  // * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} instead.
  // */
  // public Stroke getSectionOutlineStroke(int section) {
  // Comparable key = getSectionKey(section);
  // return getSectionOutlineStroke(key);
  // }

  // /**
  // * Sets the stroke used to fill a section of the pie and sends a
  // * {@link PlotChangeEvent} to all registered listeners.
  // *
  // * @param section the section index (zero-based).
  // * @param stroke the stroke (<code>null</code> permitted).
  // *
  // * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)}
  // * instead.
  // */
  // public void setSectionOutlineStroke(int section, Stroke stroke) {
  // Comparable key = getSectionKey(section);
  // setSectionOutlineStroke(key, stroke);
  // }

  /**
   * Sets the URL generator and sends a {@link PlotChangeEvent} to all
   * registered listeners.
   *
   * @param generator
   *            the generator (<code>null</code> permitted).
   *
   * @see #getURLGenerator()
   */
  public void setURLGenerator(final PieURLGenerator generator) {
    this.urlGenerator = generator;
    this.fireChangeEvent();
  }

  /**
   * Provides serialization support.
   *
   * @param stream
   *            the output stream.
   *
   * @throws IOException
   *             if there is an I/O error.
   */
  private void writeObject(final ObjectOutputStream stream)
      throws IOException {
    stream.defaultWriteObject();
    // SerialUtilities.writePaint(this.sectionPaint, stream);
    // SerialUtilities.writePaint(this.baseSectionPaint, stream);
    // SerialUtilities.writePaint(this.sectionOutlinePaint, stream);
    // SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream);
    // SerialUtilities.writeStroke(this.sectionOutlineStroke, stream);
    // SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream);
    // SerialUtilities.writePaint(this.shadowPaint, stream);
    // SerialUtilities.writePaint(this.labelPaint, stream);
    // SerialUtilities.writePaint(this.labelBackgroundPaint, stream);
    // SerialUtilities.writePaint(this.labelOutlinePaint, stream);
    // SerialUtilities.writeStroke(this.labelOutlineStroke, stream);
    // SerialUtilities.writePaint(this.labelShadowPaint, stream);
    // SerialUtilities.writePaint(this.labelLinkPaint, stream);
    // SerialUtilities.writeStroke(this.labelLinkStroke, stream);
    // SerialUtilities.writeShape(this.legendItemShape, stream);
  }

}
TOP

Related Classes of com.positive.charts.plot.PiePlot

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.