    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ---------
    028 * Plot.java
    029 * ---------
    030 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Sylvain Vieujot;
    034 *                   Jeremy Bowman;
    035 *                   Andreas Schneider;
    036 *                   Gideon Krause;
    037 *                   Nicolas Brodu;
    038 *                   Michal Krause;
    039 *                   Richard West, Advanced Micro Devices, Inc.;
    040 *                   Peter Kolb - patches 2603321, 2809117;
    041 *
    042 * Changes
    043 * -------
    044 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
    045 * 18-Sep-2001 : Updated header info and fixed DOS encoding problem (DG);
    046 * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart
    047 *               class (DG);
    048 * 23-Oct-2001 : Created renderer for LinePlot class (DG);
    049 * 07-Nov-2001 : Changed type names for ChartChangeEvent (DG);
    050 *               Tidied up some Javadoc comments (DG);
    051 * 13-Nov-2001 : Changes to allow for null axes on plots such as PiePlot (DG);
    052 *               Added plot/axis compatibility checks (DG);
    053 * 12-Dec-2001 : Changed constructors to protected, and removed unnecessary
    054 *               'throws' clauses (DG);
    055 * 13-Dec-2001 : Added tooltips (DG);
    056 * 22-Jan-2002 : Added handleClick() method, as part of implementation for
    057 *               crosshairs (DG);
    058 *               Moved tooltips reference into ChartInfo class (DG);
    059 * 23-Jan-2002 : Added test for null axes in chartChanged() method, thanks
    060 *               to Barry Evans for the bug report (number 506979 on
    061 *               SourceForge) (DG);
    062 *               Added a zoom() method (DG);
    063 * 05-Feb-2002 : Updated setBackgroundPaint(), setOutlineStroke() and
    064 *               setOutlinePaint() to better handle null values, as suggested
    065 *               by Sylvain Vieujot (DG);
    066 * 06-Feb-2002 : Added background image, plus alpha transparency for background
    067 *               and foreground (DG);
    068 * 06-Mar-2002 : Added AxisConstants interface (DG);
    069 * 26-Mar-2002 : Changed zoom method from empty to abstract (DG);
    070 * 23-Apr-2002 : Moved dataset from JFreeChart class (DG);
    071 * 11-May-2002 : Added ShapeFactory interface for getShape() methods,
    072 *               contributed by Jeremy Bowman (DG);
    073 * 28-May-2002 : Fixed bug in setSeriesPaint(int, Paint) for subplots (AS);
    074 * 25-Jun-2002 : Removed redundant imports (DG);
    075 * 30-Jul-2002 : Added 'no data' message for charts with null or empty
    076 *               datasets (DG);
    077 * 21-Aug-2002 : Added code to extend series array if necessary (refer to
    078 *               SourceForge bug id 594547 for details) (DG);
    079 * 17-Sep-2002 : Fixed bug in getSeriesOutlineStroke() method, reported by
    080 *               Andreas Schroeder (DG);
    081 * 23-Sep-2002 : Added getLegendItems() abstract method (DG);
    082 * 24-Sep-2002 : Removed firstSeriesIndex, subplots now use their own paint
    083 *               settings, there is a new mechanism for the legend to collect
    084 *               the legend items (DG);
    085 * 27-Sep-2002 : Added dataset group (DG);
    086 * 14-Oct-2002 : Moved listener storage into EventListenerList.  Changed some
    087 *               abstract methods to empty implementations (DG);
    088 * 28-Oct-2002 : Added a getBackgroundImage() method (DG);
    089 * 21-Nov-2002 : Added a plot index for identifying subplots in combined and
    090 *               overlaid charts (DG);
    091 * 22-Nov-2002 : Changed all attributes from 'protected' to 'private'.  Added
    092 *               dataAreaRatio attribute from David M O'Donnell's code (DG);
    093 * 09-Jan-2003 : Integrated fix for plot border contributed by Gideon
    094 *               Krause (DG);
    095 * 17-Jan-2003 : Moved to com.jrefinery.chart.plot (DG);
    096 * 23-Jan-2003 : Removed one constructor (DG);
    097 * 26-Mar-2003 : Implemented Serializable (DG);
    098 * 14-Jul-2003 : Moved the dataset and secondaryDataset attributes to the
    099 *               CategoryPlot and XYPlot classes (DG);
    100 * 21-Jul-2003 : Moved DrawingSupplier from CategoryPlot and XYPlot up to this
    101 *               class (DG);
    102 * 20-Aug-2003 : Implemented Cloneable (DG);
    103 * 11-Sep-2003 : Listeners and clone (NB);
    104 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
    105 * 03-Dec-2003 : Modified draw method to accept anchor (DG);
    106 * 12-Mar-2004 : Fixed clipping bug in drawNoDataMessage() method (DG);
    107 * 07-Apr-2004 : Modified string bounds calculation (DG);
    108 * 04-Nov-2004 : Added default shapes for legend items (DG);
    109 * 25-Nov-2004 : Some changes to the clone() method implementation (DG);
    110 * 23-Feb-2005 : Implemented new LegendItemSource interface (and also
    111 *               PublicCloneable) (DG);
    112 * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG);
    113 * 05-May-2005 : Removed unused draw() method (DG);
    114 * 06-Jun-2005 : Fixed bugs in equals() method (DG);
    115 * 01-Sep-2005 : Moved dataAreaRatio from here to ContourPlot (DG);
    116 * ------------- JFREECHART 1.0.x ---------------------------------------------
    117 * 30-Jun-2006 : Added background image alpha - see bug report 1514904 (DG);
    118 * 05-Sep-2006 : Implemented the MarkerChangeListener interface (DG);
    119 * 11-Jan-2007 : Added some argument checks, event notifications, and many
    120 *               API doc updates (DG);
    121 * 03-Apr-2007 : Made drawBackgroundImage() public (DG);
    122 * 07-Jun-2007 : Added new fillBackground() method to handle GradientPaint
    123 *               taking into account orientation (DG);
    124 * 25-Mar-2008 : Added fireChangeEvent() method - see patch 1914411 (DG);
    125 * 15-Aug-2008 : Added setDrawingSupplier() method with notify flag (DG);
    126 * 13-Jan-2009 : Added notify flag (DG);
    127 * 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG);
    128 * 24-Jun-2009 : Implemented AnnotationChangeListener (see patch 2809117 by
    129 *               PK) (DG);
    130 * 13-Jul-2009 : Plot background image should be clipped if necessary (DG);
    131 *
    132 */
    133
    134package org.jfree.chart.plot;
    135
    136import java.awt.AlphaComposite;
    137import java.awt.BasicStroke;
    138import java.awt.Color;
    139import java.awt.Composite;
    140import java.awt.Font;
    141import java.awt.GradientPaint;
    142import java.awt.Graphics2D;
    143import java.awt.Image;
    144import java.awt.Paint;
    145import java.awt.Shape;
    146import java.awt.Stroke;
    147import java.awt.geom.Ellipse2D;
    148import java.awt.geom.Point2D;
    149import java.awt.geom.Rectangle2D;
    150import java.io.IOException;
    151import java.io.ObjectInputStream;
    152import java.io.ObjectOutputStream;
    153import java.io.Serializable;
    154
    155import javax.swing.event.EventListenerList;
    156
    157import org.jfree.chart.JFreeChart;
    158import org.jfree.chart.LegendItemCollection;
    159import org.jfree.chart.LegendItemSource;
    160import org.jfree.chart.annotations.Annotation;
    161import org.jfree.chart.axis.AxisLocation;
    162import org.jfree.chart.entity.EntityCollection;
    163import org.jfree.chart.entity.PlotEntity;
    164import org.jfree.chart.event.AnnotationChangeEvent;
    165import org.jfree.chart.event.AnnotationChangeListener;
    166import org.jfree.chart.event.AxisChangeEvent;
    167import org.jfree.chart.event.AxisChangeListener;
    168import org.jfree.chart.event.ChartChangeEventType;
    169import org.jfree.chart.event.MarkerChangeEvent;
    170import org.jfree.chart.event.MarkerChangeListener;
    171import org.jfree.chart.event.PlotChangeEvent;
    172import org.jfree.chart.event.PlotChangeListener;
    173import org.jfree.data.general.DatasetChangeEvent;
    174import org.jfree.data.general.DatasetChangeListener;
    175import org.jfree.data.general.DatasetGroup;
    176import org.jfree.io.SerialUtilities;
    177import org.jfree.text.G2TextMeasurer;
    178import org.jfree.text.TextBlock;
    179import org.jfree.text.TextBlockAnchor;
    180import org.jfree.text.TextUtilities;
    181import org.jfree.ui.Align;
    182import org.jfree.ui.RectangleEdge;
    183import org.jfree.ui.RectangleInsets;
    184import org.jfree.util.ObjectUtilities;
    185import org.jfree.util.PaintUtilities;
    186import org.jfree.util.PublicCloneable;
    187
    188/**
    189 * The base class for all plots in JFreeChart.  The {@link JFreeChart} class
    190 * delegates the drawing of axes and data to the plot.  This base class
    191 * provides facilities common to most plot types.
    192 */
    193public abstract class Plot implements AxisChangeListener,
    194        DatasetChangeListener, AnnotationChangeListener, MarkerChangeListener,
    195        LegendItemSource, PublicCloneable, Cloneable, Serializable {
    196
    197    /** For serialization. */
    198    private static final long serialVersionUID = -8831571430103671324L;
    199
    200    /** Useful constant representing zero. */
    201    public static final Number ZERO = new Integer(0);
    202
    203    /** The default insets. */
    204    public static final RectangleInsets DEFAULT_INSETS
    205            = new RectangleInsets(4.0, 8.0, 4.0, 8.0);
    206
    207    /** The default outline stroke. */
    208    public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(0.5f,
    209            BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
    210
    211    /** The default outline color. */
    212    public static final Paint DEFAULT_OUTLINE_PAINT = Color.gray;
    213
    214    /** The default foreground alpha transparency. */
    215    public static final float DEFAULT_FOREGROUND_ALPHA = 1.0f;
    216
    217    /** The default background alpha transparency. */
    218    public static final float DEFAULT_BACKGROUND_ALPHA = 1.0f;
    219
    220    /** The default background color. */
    221    public static final Paint DEFAULT_BACKGROUND_PAINT = Color.white;
    222
    223    /** The minimum width at which the plot should be drawn. */
    224    public static final int MINIMUM_WIDTH_TO_DRAW = 10;
    225
    226    /** The minimum height at which the plot should be drawn. */
    227    public static final int MINIMUM_HEIGHT_TO_DRAW = 10;
    228
    229    /** A default box shape for legend items. */
    230    public static final Shape DEFAULT_LEGEND_ITEM_BOX
    231            = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
    232
    233    /** A default circle shape for legend items. */
    234    public static final Shape DEFAULT_LEGEND_ITEM_CIRCLE
    235            = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0);
    236
    237    /** The parent plot (<code>null</code> if this is the root plot). */
    238    private Plot parent;
    239
    240    /** The dataset group (to be used for thread synchronisation). */
    241    private DatasetGroup datasetGroup;
    242
    243    /** The message to display if no data is available. */
    244    private String noDataMessage;
    245
    246    /** The font used to display the 'no data' message. */
    247    private Font noDataMessageFont;
    248
    249    /** The paint used to draw the 'no data' message. */
    250    private transient Paint noDataMessagePaint;
    251
    252    /** Amount of blank space around the plot area. */
    253    private RectangleInsets insets;
    254
    255    /**
    256     * A flag that controls whether or not the plot outline is drawn.
    257     *
    258     * @since 1.0.6
    259     */
    260    private boolean outlineVisible;
    261
    262    /** The Stroke used to draw an outline around the plot. */
    263    private transient Stroke outlineStroke;
    264
    265    /** The Paint used to draw an outline around the plot. */
    266    private transient Paint outlinePaint;
    267
    268    /** An optional color used to fill the plot background. */
    269    private transient Paint backgroundPaint;
    270
    271    /** An optional image for the plot background. */
    272    private transient Image backgroundImage;  // not currently serialized
    273
    274    /** The alignment for the background image. */
    275    private int backgroundImageAlignment = Align.FIT;
    276
    277    /** The alpha value used to draw the background image. */
    278    private float backgroundImageAlpha = 0.5f;
    279
    280    /** The alpha-transparency for the plot. */
    281    private float foregroundAlpha;
    282
    283    /** The alpha transparency for the background paint. */
    284    private float backgroundAlpha;
    285
    286    /** The drawing supplier. */
    287    private DrawingSupplier drawingSupplier;
    288
    289    /** Storage for registered change listeners. */
    290    private transient EventListenerList listenerList;
    291
    292    /**
    293     * A flag that controls whether or not the plot will notify listeners
    294     * of changes (defaults to true, but sometimes it is useful to disable
    295     * this).
    296     *
    297     * @since 1.0.13
    298     */
    299    private boolean notify;
    300
    301    /**
    302     * Creates a new plot.
    303     */
    304    protected Plot() {
    305
    306        this.parent = null;
    307        this.insets = DEFAULT_INSETS;
    308        this.backgroundPaint = DEFAULT_BACKGROUND_PAINT;
    309        this.backgroundAlpha = DEFAULT_BACKGROUND_ALPHA;
    310        this.backgroundImage = null;
    311        this.outlineVisible = true;
    312        this.outlineStroke = DEFAULT_OUTLINE_STROKE;
    313        this.outlinePaint = DEFAULT_OUTLINE_PAINT;
    314        this.foregroundAlpha = DEFAULT_FOREGROUND_ALPHA;
    315
    316        this.noDataMessage = null;
    317        this.noDataMessageFont = new Font("SansSerif", Font.PLAIN, 12);
    318        this.noDataMessagePaint = Color.black;
    319
    320        this.drawingSupplier = new DefaultDrawingSupplier();
    321
    322        this.notify = true;
    323        this.listenerList = new EventListenerList();
    324
    325    }
    326
    327    /**
    328     * Returns the dataset group for the plot (not currently used).
    329     *
    330     * @return The dataset group.
    331     *
    332     * @see #setDatasetGroup(DatasetGroup)
    333     */
    334    public DatasetGroup getDatasetGroup() {
    335        return this.datasetGroup;
    336    }
    337
    338    /**
    339     * Sets the dataset group (not currently used).
    340     *
    341     * @param group  the dataset group (<code>null</code> permitted).
    342     *
    343     * @see #getDatasetGroup()
    344     */
    345    protected void setDatasetGroup(DatasetGroup group) {
    346        this.datasetGroup = group;
    347    }
    348
    349    /**
    350     * Returns the string that is displayed when the dataset is empty or
    351     * <code>null</code>.
    352     *
    353     * @return The 'no data' message (<code>null</code> possible).
    354     *
    355     * @see #setNoDataMessage(String)
    356     * @see #getNoDataMessageFont()
    357     * @see #getNoDataMessagePaint()
    358     */
    359    public String getNoDataMessage() {
    360        return this.noDataMessage;
    361    }
    362
    363    /**
    364     * Sets the message that is displayed when the dataset is empty or
    365     * <code>null</code>, and sends a {@link PlotChangeEvent} to all registered
    366     * listeners.
    367     *
    368     * @param message  the message (<code>null</code> permitted).
    369     *
    370     * @see #getNoDataMessage()
    371     */
    372    public void setNoDataMessage(String message) {
    373        this.noDataMessage = message;
    374        fireChangeEvent();
    375    }
    376
    377    /**
    378     * Returns the font used to display the 'no data' message.
    379     *
    380     * @return The font (never <code>null</code>).
    381     *
    382     * @see #setNoDataMessageFont(Font)
    383     * @see #getNoDataMessage()
    384     */
    385    public Font getNoDataMessageFont() {
    386        return this.noDataMessageFont;
    387    }
    388
    389    /**
    390     * Sets the font used to display the 'no data' message and sends a
    391     * {@link PlotChangeEvent} to all registered listeners.
    392     *
    393     * @param font  the font (<code>null</code> not permitted).
    394     *
    395     * @see #getNoDataMessageFont()
    396     */
    397    public void setNoDataMessageFont(Font font) {
    398        if (font == null) {
    399            throw new IllegalArgumentException("Null 'font' argument.");
    400        }
    401        this.noDataMessageFont = font;
    402        fireChangeEvent();
    403    }
    404
    405    /**
    406     * Returns the paint used to display the 'no data' message.
    407     *
    408     * @return The paint (never <code>null</code>).
    409     *
    410     * @see #setNoDataMessagePaint(Paint)
    411     * @see #getNoDataMessage()
    412     */
    413    public Paint getNoDataMessagePaint() {
    414        return this.noDataMessagePaint;
    415    }
    416
    417    /**
    418     * Sets the paint used to display the 'no data' message and sends a
    419     * {@link PlotChangeEvent} to all registered listeners.
    420     *
    421     * @param paint  the paint (<code>null</code> not permitted).
    422     *
    423     * @see #getNoDataMessagePaint()
    424     */
    425    public void setNoDataMessagePaint(Paint paint) {
    426        if (paint == null) {
    427            throw new IllegalArgumentException("Null 'paint' argument.");
    428        }
    429        this.noDataMessagePaint = paint;
    430        fireChangeEvent();
    431    }
    432
    433    /**
    434     * Returns a short string describing the plot type.
    435     * <P>
    436     * Note: this gets used in the chart property editing user interface,
    437     * but there needs to be a better mechanism for identifying the plot type.
    438     *
    439     * @return A short string describing the plot type (never
    440     *     <code>null</code>).
    441     */
    442    public abstract String getPlotType();
    443
    444    /**
    445     * Returns the parent plot (or <code>null</code> if this plot is not part
    446     * of a combined plot).
    447     *
    448     * @return The parent plot.
    449     *
    450     * @see #setParent(Plot)
    451     * @see #getRootPlot()
    452     */
    453    public Plot getParent() {
    454        return this.parent;
    455    }
    456
    457    /**
    458     * Sets the parent plot.  This method is intended for internal use, you
    459     * shouldn't need to call it directly.
    460     *
    461     * @param parent  the parent plot (<code>null</code> permitted).
    462     *
    463     * @see #getParent()
    464     */
    465    public void setParent(Plot parent) {
    466        this.parent = parent;
    467    }
    468
    469    /**
    470     * Returns the root plot.
    471     *
    472     * @return The root plot.
    473     *
    474     * @see #getParent()
    475     */
    476    public Plot getRootPlot() {
    477
    478        Plot p = getParent();
    479        if (p == null) {
    480            return this;
    481        }
    482        return p.getRootPlot();
    483
    484    }
    485
    486    /**
    487     * Returns <code>true</code> if this plot is part of a combined plot
    488     * structure (that is, {@link #getParent()} returns a non-<code>null</code>
    489     * value), and <code>false</code> otherwise.
    490     *
    491     * @return <code>true</code> if this plot is part of a combined plot
    492     *         structure.
    493     *
    494     * @see #getParent()
    495     */
    496    public boolean isSubplot() {
    497        return (getParent() != null);
    498    }
    499
    500    /**
    501     * Returns the insets for the plot area.
    502     *
    503     * @return The insets (never <code>null</code>).
    504     *
    505     * @see #setInsets(RectangleInsets)
    506     */
    507    public RectangleInsets getInsets() {
    508        return this.insets;
    509    }
    510
    511    /**
    512     * Sets the insets for the plot and sends a {@link PlotChangeEvent} to
    513     * all registered listeners.
    514     *
    515     * @param insets  the new insets (<code>null</code> not permitted).
    516     *
    517     * @see #getInsets()
    518     * @see #setInsets(RectangleInsets, boolean)
    519     */
    520    public void setInsets(RectangleInsets insets) {
    521        setInsets(insets, true);
    522    }
    523
    524    /**
    525     * Sets the insets for the plot and, if requested,  and sends a
    526     * {@link PlotChangeEvent} to all registered listeners.
    527     *
    528     * @param insets  the new insets (<code>null</code> not permitted).
    529     * @param notify  a flag that controls whether the registered listeners are
    530     *                notified.
    531     *
    532     * @see #getInsets()
    533     * @see #setInsets(RectangleInsets)
    534     */
    535    public void setInsets(RectangleInsets insets, boolean notify) {
    536        if (insets == null) {
    537            throw new IllegalArgumentException("Null 'insets' argument.");
    538        }
    539        if (!this.insets.equals(insets)) {
    540            this.insets = insets;
    541            if (notify) {
    542                fireChangeEvent();
    543            }
    544        }
    545
    546    }
    547
    548    /**
    549     * Returns the background color of the plot area.
    550     *
    551     * @return The paint (possibly <code>null</code>).
    552     *
    553     * @see #setBackgroundPaint(Paint)
    554     */
    555    public Paint getBackgroundPaint() {
    556        return this.backgroundPaint;
    557    }
    558
    559    /**
    560     * Sets the background color of the plot area and sends a
    561     * {@link PlotChangeEvent} to all registered listeners.
    562     *
    563     * @param paint  the paint (<code>null</code> permitted).
    564     *
    565     * @see #getBackgroundPaint()
    566     */
    567    public void setBackgroundPaint(Paint paint) {
    568
    569        if (paint == null) {
    570            if (this.backgroundPaint != null) {
    571                this.backgroundPaint = null;
    572                fireChangeEvent();
    573            }
    574        }
    575        else {
    576            if (this.backgroundPaint != null) {
    577                if (this.backgroundPaint.equals(paint)) {
    578                    return;  // nothing to do
    579                }
    580            }
    581            this.backgroundPaint = paint;
    582            fireChangeEvent();
    583        }
    584
    585    }
    586
    587    /**
    588     * Returns the alpha transparency of the plot area background.
    589     *
    590     * @return The alpha transparency.
    591     *
    592     * @see #setBackgroundAlpha(float)
    593     */
    594    public float getBackgroundAlpha() {
    595        return this.backgroundAlpha;
    596    }
    597
    598    /**
    599     * Sets the alpha transparency of the plot area background, and notifies
    600     * registered listeners that the plot has been modified.
    601     *
    602     * @param alpha the new alpha value (in the range 0.0f to 1.0f).
    603     *
    604     * @see #getBackgroundAlpha()
    605     */
    606    public void setBackgroundAlpha(float alpha) {
    607        if (this.backgroundAlpha != alpha) {
    608            this.backgroundAlpha = alpha;
    609            fireChangeEvent();
    610        }
    611    }
    612
    613    /**
    614     * Returns the drawing supplier for the plot.
    615     *
    616     * @return The drawing supplier (possibly <code>null</code>).
    617     *
    618     * @see #setDrawingSupplier(DrawingSupplier)
    619     */
    620    public DrawingSupplier getDrawingSupplier() {
    621        DrawingSupplier result = null;
    622        Plot p = getParent();
    623        if (p != null) {
    624            result = p.getDrawingSupplier();
    625        }
    626        else {
    627            result = this.drawingSupplier;
    628        }
    629        return result;
    630    }
    631
    632    /**
    633     * Sets the drawing supplier for the plot and sends a
    634     * {@link PlotChangeEvent} to all registered listeners.  The drawing
    635     * supplier is responsible for supplying a limitless (possibly repeating)
    636     * sequence of <code>Paint</code>, <code>Stroke</code> and
    637     * <code>Shape</code> objects that the plot's renderer(s) can use to
    638     * populate its (their) tables.
    639     *
    640     * @param supplier  the new supplier.
    641     *
    642     * @see #getDrawingSupplier()
    643     */
    644    public void setDrawingSupplier(DrawingSupplier supplier) {
    645        this.drawingSupplier = supplier;
    646        fireChangeEvent();
    647    }
    648
    649    /**
    650     * Sets the drawing supplier for the plot and, if requested, sends a
    651     * {@link PlotChangeEvent} to all registered listeners.  The drawing
    652     * supplier is responsible for supplying a limitless (possibly repeating)
    653     * sequence of <code>Paint</code>, <code>Stroke</code> and
    654     * <code>Shape</code> objects that the plot's renderer(s) can use to
    655     * populate its (their) tables.
    656     *
    657     * @param supplier  the new supplier.
    658     * @param notify  notify listeners?
    659     *
    660     * @see #getDrawingSupplier()
    661     *
    662     * @since 1.0.11
    663     */
    664    public void setDrawingSupplier(DrawingSupplier supplier, boolean notify) {
    665        this.drawingSupplier = supplier;
    666        if (notify) {
    667            fireChangeEvent();
    668        }
    669    }
    670
    671    /**
    672     * Returns the background image that is used to fill the plot's background
    673     * area.
    674     *
    675     * @return The image (possibly <code>null</code>).
    676     *
    677     * @see #setBackgroundImage(Image)
    678     */
    679    public Image getBackgroundImage() {
    680        return this.backgroundImage;
    681    }
    682
    683    /**
    684     * Sets the background image for the plot and sends a
    685     * {@link PlotChangeEvent} to all registered listeners.
    686     *
    687     * @param image  the image (<code>null</code> permitted).
    688     *
    689     * @see #getBackgroundImage()
    690     */
    691    public void setBackgroundImage(Image image) {
    692        this.backgroundImage = image;
    693        fireChangeEvent();
    694    }
    695
    696    /**
    697     * Returns the background image alignment. Alignment constants are defined
    698     * in the <code>org.jfree.ui.Align</code> class in the JCommon class
    699     * library.
    700     *
    701     * @return The alignment.
    702     *
    703     * @see #setBackgroundImageAlignment(int)
    704     */
    705    public int getBackgroundImageAlignment() {
    706        return this.backgroundImageAlignment;
    707    }
    708
    709    /**
    710     * Sets the alignment for the background image and sends a
    711     * {@link PlotChangeEvent} to all registered listeners.  Alignment options
    712     * are defined by the {@link org.jfree.ui.Align} class in the JCommon
    713     * class library.
    714     *
    715     * @param alignment  the alignment.
    716     *
    717     * @see #getBackgroundImageAlignment()
    718     */
    719    public void setBackgroundImageAlignment(int alignment) {
    720        if (this.backgroundImageAlignment != alignment) {
    721            this.backgroundImageAlignment = alignment;
    722            fireChangeEvent();
    723        }
    724    }
    725
    726    /**
    727     * Returns the alpha transparency used to draw the background image.  This
    728     * is a value in the range 0.0f to 1.0f, where 0.0f is fully transparent
    729     * and 1.0f is fully opaque.
    730     *
    731     * @return The alpha transparency.
    732     *
    733     * @see #setBackgroundImageAlpha(float)
    734     */
    735    public float getBackgroundImageAlpha() {
    736        return this.backgroundImageAlpha;
    737    }
    738
    739    /**
    740     * Sets the alpha transparency used when drawing the background image.
    741     *
    742     * @param alpha  the alpha transparency (in the range 0.0f to 1.0f, where
    743     *     0.0f is fully transparent, and 1.0f is fully opaque).
    744     *
    745     * @throws IllegalArgumentException if <code>alpha</code> is not within
    746     *     the specified range.
    747     *
    748     * @see #getBackgroundImageAlpha()
    749     */
    750    public void setBackgroundImageAlpha(float alpha) {
    751        if (alpha < 0.0f || alpha > 1.0f)
    752            throw new IllegalArgumentException(
    753                    "The 'alpha' value must be in the range 0.0f to 1.0f.");
    754        if (this.backgroundImageAlpha != alpha) {
    755            this.backgroundImageAlpha = alpha;
    756            fireChangeEvent();
    757        }
    758    }
    759
    760    /**
    761     * Returns the flag that controls whether or not the plot outline is
    762     * drawn.  The default value is <code>true</code>.  Note that for
    763     * historical reasons, the plot's outline paint and stroke can take on
    764     * <code>null</code> values, in which case the outline will not be drawn
    765     * even if this flag is set to <code>true</code>.
    766     *
    767     * @return The outline visibility flag.
    768     *
    769     * @since 1.0.6
    770     *
    771     * @see #setOutlineVisible(boolean)
    772     */
    773    public boolean isOutlineVisible() {
    774        return this.outlineVisible;
    775    }
    776
    777    /**
    778     * Sets the flag that controls whether or not the plot's outline is
    779     * drawn, and sends a {@link PlotChangeEvent} to all registered listeners.
    780     *
    781     * @param visible  the new flag value.
    782     *
    783     * @since 1.0.6
    784     *
    785     * @see #isOutlineVisible()
    786     */
    787    public void setOutlineVisible(boolean visible) {
    788        this.outlineVisible = visible;
    789        fireChangeEvent();
    790    }
    791
    792    /**
    793     * Returns the stroke used to outline the plot area.
    794     *
    795     * @return The stroke (possibly <code>null</code>).
    796     *
    797     * @see #setOutlineStroke(Stroke)
    798     */
    799    public Stroke getOutlineStroke() {
    800        return this.outlineStroke;
    801    }
    802
    803    /**
    804     * Sets the stroke used to outline the plot area and sends a
    805     * {@link PlotChangeEvent} to all registered listeners. If you set this
    806     * attribute to <code>null</code>, no outline will be drawn.
    807     *
    808     * @param stroke  the stroke (<code>null</code> permitted).
    809     *
    810     * @see #getOutlineStroke()
    811     */
    812    public void setOutlineStroke(Stroke stroke) {
    813        if (stroke == null) {
    814            if (this.outlineStroke != null) {
    815                this.outlineStroke = null;
    816                fireChangeEvent();
    817            }
    818        }
    819        else {
    820            if (this.outlineStroke != null) {
    821                if (this.outlineStroke.equals(stroke)) {
    822                    return;  // nothing to do
    823                }
    824            }
    825            this.outlineStroke = stroke;
    826            fireChangeEvent();
    827        }
    828    }
    829
    830    /**
    831     * Returns the color used to draw the outline of the plot area.
    832     *
    833     * @return The color (possibly <code>null</code>).
    834     *
    835     * @see #setOutlinePaint(Paint)
    836     */
    837    public Paint getOutlinePaint() {
    838        return this.outlinePaint;
    839    }
    840
    841    /**
    842     * Sets the paint used to draw the outline of the plot area and sends a
    843     * {@link PlotChangeEvent} to all registered listeners.  If you set this
    844     * attribute to <code>null</code>, no outline will be drawn.
    845     *
    846     * @param paint  the paint (<code>null</code> permitted).
    847     *
    848     * @see #getOutlinePaint()
    849     */
    850    public void setOutlinePaint(Paint paint) {
    851        if (paint == null) {
    852            if (this.outlinePaint != null) {
    853                this.outlinePaint = null;
    854                fireChangeEvent();
    855            }
    856        }
    857        else {
    858            if (this.outlinePaint != null) {
    859                if (this.outlinePaint.equals(paint)) {
    860                    return;  // nothing to do
    861                }
    862            }
    863            this.outlinePaint = paint;
    864            fireChangeEvent();
    865        }
    866    }
    867
    868    /**
    869     * Returns the alpha-transparency for the plot foreground.
    870     *
    871     * @return The alpha-transparency.
    872     *
    873     * @see #setForegroundAlpha(float)
    874     */
    875    public float getForegroundAlpha() {
    876        return this.foregroundAlpha;
    877    }
    878
    879    /**
    880     * Sets the alpha-transparency for the plot and sends a
    881     * {@link PlotChangeEvent} to all registered listeners.
    882     *
    883     * @param alpha  the new alpha transparency.
    884     *
    885     * @see #getForegroundAlpha()
    886     */
    887    public void setForegroundAlpha(float alpha) {
    888        if (this.foregroundAlpha != alpha) {
    889            this.foregroundAlpha = alpha;
    890            fireChangeEvent();
    891        }
    892    }
    893
    894    /**
    895     * Returns the legend items for the plot.  By default, this method returns
    896     * <code>null</code>.  Subclasses should override to return a
    897     * {@link LegendItemCollection}.
    898     *
    899     * @return The legend items for the plot (possibly <code>null</code>).
    900     */
    901    public LegendItemCollection getLegendItems() {
    902        return null;
    903    }
    904
    905    /**
    906     * Returns a flag that controls whether or not change events are sent to
    907     * registered listeners.
    908     *
    909     * @return A boolean.
    910     *
    911     * @see #setNotify(boolean)
    912     *
    913     * @since 1.0.13
    914     */
    915    public boolean isNotify() {
    916        return this.notify;
    917    }
    918
    919    /**
    920     * Sets a flag that controls whether or not listeners receive
    921     * {@link PlotChangeEvent} notifications.
    922     *
    923     * @param notify  a boolean.
    924     *
    925     * @see #isNotify()
    926     *
    927     * @since 1.0.13
    928     */
    929    public void setNotify(boolean notify) {
    930        this.notify = notify;
    931        // if the flag is being set to true, there may be queued up changes...
    932        if (notify) {
    933            notifyListeners(new PlotChangeEvent(this));
    934        }
    935    }
    936
    937    /**
    938     * Registers an object for notification of changes to the plot.
    939     *
    940     * @param listener  the object to be registered.
    941     *
    942     * @see #removeChangeListener(PlotChangeListener)
    943     */
    944    public void addChangeListener(PlotChangeListener listener) {
    945        this.listenerList.add(PlotChangeListener.class, listener);
    946    }
    947
    948    /**
    949     * Unregisters an object for notification of changes to the plot.
    950     *
    951     * @param listener  the object to be unregistered.
    952     *
    953     * @see #addChangeListener(PlotChangeListener)
    954     */
    955    public void removeChangeListener(PlotChangeListener listener) {
    956        this.listenerList.remove(PlotChangeListener.class, listener);
    957    }
    958
    959    /**
    960     * Notifies all registered listeners that the plot has been modified.
    961     *
    962     * @param event  information about the change event.
    963     */
    964    public void notifyListeners(PlotChangeEvent event) {
    965        // if the 'notify' flag has been switched to false, we don't notify
    966        // the listeners
    967        if (!this.notify) {
    968            return;
    969        }
    970        Object[] listeners = this.listenerList.getListenerList();
    971        for (int i = listeners.length - 2; i >= 0; i -= 2) {
    972            if (listeners[i] == PlotChangeListener.class) {
    973                ((PlotChangeListener) listeners[i + 1]).plotChanged(event);
    974            }
    975        }
    976    }
    977
    978    /**
    979     * Sends a {@link PlotChangeEvent} to all registered listeners.
    980     *
    981     * @since 1.0.10
    982     */
    983    protected void fireChangeEvent() {
    984        notifyListeners(new PlotChangeEvent(this));
    985    }
    986
    987    /**
    988     * Draws the plot within the specified area.  The anchor is a point on the
    989     * chart that is specified externally (for instance, it may be the last
    990     * point of the last mouse click performed by the user) - plots can use or
    991     * ignore this value as they see fit.
    992     * <br><br>
    993     * Subclasses need to provide an implementation of this method, obviously.
    994     *
    995     * @param g2  the graphics device.
    996     * @param area  the plot area.
    997     * @param anchor  the anchor point (<code>null</code> permitted).
    998     * @param parentState  the parent state (if any).
    999     * @param info  carries back plot rendering info.
    1000     */
    1001    public abstract void draw(Graphics2D g2,
    1002                              Rectangle2D area,
    1003                              Point2D anchor,
    1004                              PlotState parentState,
    1005                              PlotRenderingInfo info);
    1006
    1007    /**
    1008     * Draws the plot background (the background color and/or image).
    1009     * <P>
    1010     * This method will be called during the chart drawing process and is
    1011     * declared public so that it can be accessed by the renderers used by
    1012     * certain subclasses.  You shouldn't need to call this method directly.
    1013     *
    1014     * @param g2  the graphics device.
    1015     * @param area  the area within which the plot should be drawn.
    1016     */
    1017    public void drawBackground(Graphics2D g2, Rectangle2D area) {
    1018        // some subclasses override this method completely, so don't put
    1019        // anything here that *must* be done
    1020        fillBackground(g2, area);
    1021        drawBackgroundImage(g2, area);
    1022    }
    1023
    1024    /**
    1025     * Fills the specified area with the background paint.
    1026     *
    1027     * @param g2  the graphics device.
    1028     * @param area  the area.
    1029     *
    1030     * @see #getBackgroundPaint()
    1031     * @see #getBackgroundAlpha()
    1032     * @see #fillBackground(Graphics2D, Rectangle2D, PlotOrientation)
    1033     */
    1034    protected void fillBackground(Graphics2D g2, Rectangle2D area) {
    1035        fillBackground(g2, area, PlotOrientation.VERTICAL);
    1036    }
    1037
    1038    /**
    1039     * Fills the specified area with the background paint.  If the background
    1040     * paint is an instance of <code>GradientPaint</code>, the gradient will
    1041     * run in the direction suggested by the plot's orientation.
    1042     *
    1043     * @param g2  the graphics target.
    1044     * @param area  the plot area.
    1045     * @param orientation  the plot orientation (<code>null</code> not
    1046     *         permitted).
    1047     *
    1048     * @since 1.0.6
    1049     */
    1050    protected void fillBackground(Graphics2D g2, Rectangle2D area,
    1051            PlotOrientation orientation) {
    1052        if (orientation == null) {
    1053            throw new IllegalArgumentException("Null 'orientation' argument.");
    1054        }
    1055        if (this.backgroundPaint == null) {
    1056            return;
    1057        }
    1058        Paint p = this.backgroundPaint;
    1059        if (p instanceof GradientPaint) {
    1060            GradientPaint gp = (GradientPaint) p;
    1061            if (orientation == PlotOrientation.VERTICAL) {
    1062                p = new GradientPaint((float) area.getCenterX(),
    1063                        (float) area.getMaxY(), gp.getColor1(),
    1064                        (float) area.getCenterX(), (float) area.getMinY(),
    1065                        gp.getColor2());
    1066            }
    1067            else if (orientation == PlotOrientation.HORIZONTAL) {
    1068                p = new GradientPaint((float) area.getMinX(),
    1069                        (float) area.getCenterY(), gp.getColor1(),
    1070                        (float) area.getMaxX(), (float) area.getCenterY(),
    1071                        gp.getColor2());
    1072            }
    1073        }
    1074        Composite originalComposite = g2.getComposite();
    1075        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    1076                this.backgroundAlpha));
    1077        g2.setPaint(p);
    1078        g2.fill(area);
    1079        g2.setComposite(originalComposite);
    1080    }
    1081
    1082    /**
    1083     * Draws the background image (if there is one) aligned within the
    1084     * specified area.
    1085     *
    1086     * @param g2  the graphics device.
    1087     * @param area  the area.
    1088     *
    1089     * @see #getBackgroundImage()
    1090     * @see #getBackgroundImageAlignment()
    1091     * @see #getBackgroundImageAlpha()
    1092     */
    1093    public void drawBackgroundImage(Graphics2D g2, Rectangle2D area) {
    1094        if (this.backgroundImage == null) {
    1095            return;  // nothing to do
    1096        }
    1097        Composite savedComposite = g2.getComposite();
    1098        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
    1099                this.backgroundImageAlpha));
    1100        Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0,
    1101                this.backgroundImage.getWidth(null),
    1102                this.backgroundImage.getHeight(null));
    1103        Align.align(dest, area, this.backgroundImageAlignment);
    1104        Shape savedClip = g2.getClip();
    1105        g2.clip(area);
    1106        g2.drawImage(this.backgroundImage, (int) dest.getX(),
    1107                (int) dest.getY(), (int) dest.getWidth() + 1,
    1108                (int) dest.getHeight() + 1, null);
    1109        g2.setClip(savedClip);
    1110        g2.setComposite(savedComposite);
    1111    }
    1112
    1113    /**
    1114     * Draws the plot outline.  This method will be called during the chart
    1115     * drawing process and is declared public so that it can be accessed by the
    1116     * renderers used by certain subclasses. You shouldn't need to call this
    1117     * method directly.
    1118     *
    1119     * @param g2  the graphics device.
    1120     * @param area  the area within which the plot should be drawn.
    1121     */
    1122    public void drawOutline(Graphics2D g2, Rectangle2D area) {
    1123        if (!this.outlineVisible) {
    1124            return;
    1125        }
    1126        if ((this.outlineStroke != null) && (this.outlinePaint != null)) {
    1127            g2.setStroke(this.outlineStroke);
    1128            g2.setPaint(this.outlinePaint);
    1129            g2.draw(area);
    1130        }
    1131    }
    1132
    1133    /**
    1134     * Draws a message to state that there is no data to plot.
    1135     *
    1136     * @param g2  the graphics device.
    1137     * @param area  the area within which the plot should be drawn.
    1138     */
    1139    protected void drawNoDataMessage(Graphics2D g2, Rectangle2D area) {
    1140        Shape savedClip = g2.getClip();
    1141        g2.clip(area);
    1142        String message = this.noDataMessage;
    1143        if (message != null) {
    1144            g2.setFont(this.noDataMessageFont);
    1145            g2.setPaint(this.noDataMessagePaint);
    1146            TextBlock block = TextUtilities.createTextBlock(
    1147                    this.noDataMessage, this.noDataMessageFont,
    1148                    this.noDataMessagePaint, 0.9f * (float) area.getWidth(),
    1149                    new G2TextMeasurer(g2));
    1150            block.draw(g2, (float) area.getCenterX(),
    1151                    (float) area.getCenterY(), TextBlockAnchor.CENTER);
    1152        }
    1153        g2.setClip(savedClip);
    1154    }
    1155
    1156    /**
    1157     * Creates a plot entity that contains a reference to the plot and the
    1158     * data area as shape.
    1159     *
    1160     * @param dataArea  the data area used as hot spot for the entity.
    1161     * @param plotState  the plot rendering info containing a reference to the
    1162     *     EntityCollection.
    1163     * @param toolTip  the tool tip (defined in the respective Plot
    1164     *     subclass) (<code>null</code> permitted).
    1165     * @param urlText  the url (defined in the respective Plot subclass)
    1166     *     (<code>null</code> permitted).
    1167     *
    1168     *  @since 1.0.13
    1169     */
    1170    protected void createAndAddEntity(Rectangle2D dataArea,
    1171            PlotRenderingInfo plotState, String toolTip, String urlText) {
    1172        if (plotState != null && plotState.getOwner() != null) {
    1173            EntityCollection e = plotState.getOwner().getEntityCollection();
    1174            if (e != null) {
    1175                e.add(new PlotEntity(dataArea, this, toolTip, urlText));
    1176            }
    1177        }
    1178    }
    1179
    1180    /**
    1181     * Handles a 'click' on the plot.  Since the plot does not maintain any
    1182     * information about where it has been drawn, the plot rendering info is
    1183     * supplied as an argument so that the plot dimensions can be determined.
    1184     *
    1185     * @param x  the x coordinate (in Java2D space).
    1186     * @param y  the y coordinate (in Java2D space).
    1187     * @param info  an object containing information about the dimensions of
    1188     *              the plot.
    1189     */
    1190    public void handleClick(int x, int y, PlotRenderingInfo info) {
    1191        // provides a 'no action' default
    1192    }
    1193
    1194    /**
    1195     * Performs a zoom on the plot.  Subclasses should override if zooming is
    1196     * appropriate for the type of plot.
    1197     *
    1198     * @param percent  the zoom percentage.
    1199     */
    1200    public void zoom(double percent) {
    1201        // do nothing by default.
    1202    }
    1203
    1204    /**
    1205     * Receives notification of a change to an {@link Annotation} added to
    1206     * this plot.
    1207     *
    1208     * @param event  information about the event (not used here).
    1209     *
    1210     * @since 1.0.14
    1211     */
    1212    public void annotationChanged(AnnotationChangeEvent event) {
    1213        fireChangeEvent();
    1214    }
    1215
    1216    /**
    1217     * Receives notification of a change to one of the plot's axes.
    1218     *
    1219     * @param event  information about the event (not used here).
    1220     */
    1221    public void axisChanged(AxisChangeEvent event) {
    1222        fireChangeEvent();
    1223    }
    1224
    1225    /**
    1226     * Receives notification of a change to the plot's dataset.
    1227     * <P>
    1228     * The plot reacts by passing on a plot change event to all registered
    1229     * listeners.
    1230     *
    1231     * @param event  information about the event (not used here).
    1232     */
    1233    public void datasetChanged(DatasetChangeEvent event) {
    1234        PlotChangeEvent newEvent = new PlotChangeEvent(this);
    1235        newEvent.setType(ChartChangeEventType.DATASET_UPDATED);
    1236        notifyListeners(newEvent);
    1237    }
    1238
    1239    /**
    1240     * Receives notification of a change to a marker that is assigned to the
    1241     * plot.
    1242     *
    1243     * @param event  the event.
    1244     *
    1245     * @since 1.0.3
    1246     */
    1247    public void markerChanged(MarkerChangeEvent event) {
    1248        fireChangeEvent();
    1249    }
    1250
    1251    /**
    1252     * Adjusts the supplied x-value.
    1253     *
    1254     * @param x  the x-value.
    1255     * @param w1  width 1.
    1256     * @param w2  width 2.
    1257     * @param edge  the edge (left or right).
    1258     *
    1259     * @return The adjusted x-value.
    1260     */
    1261    protected double getRectX(double x, double w1, double w2,
    1262                              RectangleEdge edge) {
    1263
    1264        double result = x;
    1265        if (edge == RectangleEdge.LEFT) {
    1266            result = result + w1;
    1267        }
    1268        else if (edge == RectangleEdge.RIGHT) {
    1269            result = result + w2;
    1270        }
    1271        return result;
    1272
    1273    }
    1274
    1275    /**
    1276     * Adjusts the supplied y-value.
    1277     *
    1278     * @param y  the x-value.
    1279     * @param h1  height 1.
    1280     * @param h2  height 2.
    1281     * @param edge  the edge (top or bottom).
    1282     *
    1283     * @return The adjusted y-value.
    1284     */
    1285    protected double getRectY(double y, double h1, double h2,
    1286                              RectangleEdge edge) {
    1287
    1288        double result = y;
    1289        if (edge == RectangleEdge.TOP) {
    1290            result = result + h1;
    1291        }
    1292        else if (edge == RectangleEdge.BOTTOM) {
    1293            result = result + h2;
    1294        }
    1295        return result;
    1296
    1297    }
    1298
    1299    /**
    1300     * Tests this plot for equality with another object.
    1301     *
    1302     * @param obj  the object (<code>null</code> permitted).
    1303     *
    1304     * @return <code>true</code> or <code>false</code>.
    1305     */
    1306    public boolean equals(Object obj) {
    1307        if (obj == this) {
    1308            return true;
    1309        }
    1310        if (!(obj instanceof Plot)) {
    1311            return false;
    1312        }
    1313        Plot that = (Plot) obj;
    1314        if (!ObjectUtilities.equal(this.noDataMessage, that.noDataMessage)) {
    1315            return false;
    1316        }
    1317        if (!ObjectUtilities.equal(
    1318            this.noDataMessageFont, that.noDataMessageFont
    1319        )) {
    1320            return false;
    1321        }
    1322        if (!PaintUtilities.equal(this.noDataMessagePaint,
    1323                that.noDataMessagePaint)) {
    1324            return false;
    1325        }
    1326        if (!ObjectUtilities.equal(this.insets, that.insets)) {
    1327            return false;
    1328        }
    1329        if (this.outlineVisible != that.outlineVisible) {
    1330            return false;
    1331        }
    1332        if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) {
    1333            return false;
    1334        }
    1335        if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
    1336            return false;
    1337        }
    1338        if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
    1339            return false;
    1340        }
    1341        if (!ObjectUtilities.equal(this.backgroundImage,
    1342                that.backgroundImage)) {
    1343            return false;
    1344        }
    1345        if (this.backgroundImageAlignment != that.backgroundImageAlignment) {
    1346            return false;
    1347        }
    1348        if (this.backgroundImageAlpha != that.backgroundImageAlpha) {
    1349            return false;
    1350        }
    1351        if (this.foregroundAlpha != that.foregroundAlpha) {
    1352            return false;
    1353        }
    1354        if (this.backgroundAlpha != that.backgroundAlpha) {
    1355            return false;
    1356        }
    1357        if (!this.drawingSupplier.equals(that.drawingSupplier)) {
    1358            return false;
    1359        }
    1360        if (this.notify != that.notify) {
    1361            return false;
    1362        }
    1363        return true;
    1364    }
    1365
    1366    /**
    1367     * Creates a clone of the plot.
    1368     *
    1369     * @return A clone.
    1370     *
    1371     * @throws CloneNotSupportedException if some component of the plot does not
    1372     *         support cloning.
    1373     */
    1374    public Object clone() throws CloneNotSupportedException {
    1375
    1376        Plot clone = (Plot) super.clone();
    1377        // private Plot parent <-- don't clone the parent plot, but take care
    1378        // childs in combined plots instead
    1379        if (this.datasetGroup != null) {
    1380            clone.datasetGroup
    1381                = (DatasetGroup) ObjectUtilities.clone(this.datasetGroup);
    1382        }
    1383        clone.drawingSupplier
    1384            = (DrawingSupplier) ObjectUtilities.clone(this.drawingSupplier);
    1385        clone.listenerList = new EventListenerList();
    1386        return clone;
    1387
    1388    }
    1389
    1390    /**
    1391     * Provides serialization support.
    1392     *
    1393     * @param stream  the output stream.
    1394     *
    1395     * @throws IOException  if there is an I/O error.
    1396     */
    1397    private void writeObject(ObjectOutputStream stream) throws IOException {
    1398        stream.defaultWriteObject();
    1399        SerialUtilities.writePaint(this.noDataMessagePaint, stream);
    1400        SerialUtilities.writeStroke(this.outlineStroke, stream);
    1401        SerialUtilities.writePaint(this.outlinePaint, stream);
    1402        // backgroundImage
    1403        SerialUtilities.writePaint(this.backgroundPaint, stream);
    1404    }
    1405
    1406    /**
    1407     * Provides serialization support.
    1408     *
    1409     * @param stream  the input stream.
    1410     *
    1411     * @throws IOException  if there is an I/O error.
    1412     * @throws ClassNotFoundException  if there is a classpath problem.
    1413     */
    1414    private void readObject(ObjectInputStream stream)
    1415        throws IOException, ClassNotFoundException {
    1416        stream.defaultReadObject();
    1417        this.noDataMessagePaint = SerialUtilities.readPaint(stream);
    1418        this.outlineStroke = SerialUtilities.readStroke(stream);
    1419        this.outlinePaint = SerialUtilities.readPaint(stream);
    1420        // backgroundImage
    1421        this.backgroundPaint = SerialUtilities.readPaint(stream);
    1422
    1423        this.listenerList = new EventListenerList();
    1424
    1425    }
    1426
    1427    /**
    1428     * Resolves a domain axis location for a given plot orientation.
    1429     *
    1430     * @param location  the location (<code>null</code> not permitted).
    1431     * @param orientation  the orientation (<code>null</code> not permitted).
    1432     *
    1433     * @return The edge (never <code>null</code>).
    1434     */
    1435    public static RectangleEdge resolveDomainAxisLocation(
    1436            AxisLocation location, PlotOrientation orientation) {
    1437
    1438        if (location == null) {
    1439            throw new IllegalArgumentException("Null 'location' argument.");
    1440        }
    1441        if (orientation == null) {
    1442            throw new IllegalArgumentException("Null 'orientation' argument.");
    1443        }
    1444
    1445        RectangleEdge result = null;
    1446
    1447        if (location == AxisLocation.TOP_OR_RIGHT) {
    1448            if (orientation == PlotOrientation.HORIZONTAL) {
    1449                result = RectangleEdge.RIGHT;
    1450            }
    1451            else if (orientation == PlotOrientation.VERTICAL) {
    1452                result = RectangleEdge.TOP;
    1453            }
    1454        }
    1455        else if (location == AxisLocation.TOP_OR_LEFT) {
    1456            if (orientation == PlotOrientation.HORIZONTAL) {
    1457                result = RectangleEdge.LEFT;
    1458            }
    1459            else if (orientation == PlotOrientation.VERTICAL) {
    1460                result = RectangleEdge.TOP;
    1461            }
    1462        }
    1463        else if (location == AxisLocation.BOTTOM_OR_RIGHT) {
    1464            if (orientation == PlotOrientation.HORIZONTAL) {
    1465                result = RectangleEdge.RIGHT;
    1466            }
    1467            else if (orientation == PlotOrientation.VERTICAL) {
    1468                result = RectangleEdge.BOTTOM;
    1469            }
    1470        }
    1471        else if (location == AxisLocation.BOTTOM_OR_LEFT) {
    1472            if (orientation == PlotOrientation.HORIZONTAL) {
    1473                result = RectangleEdge.LEFT;
    1474            }
    1475            else if (orientation == PlotOrientation.VERTICAL) {
    1476                result = RectangleEdge.BOTTOM;
    1477            }
    1478        }
    1479        // the above should cover all the options...
    1480        if (result == null) {
    1481            throw new IllegalStateException("resolveDomainAxisLocation()");
    1482        }
    1483        return result;
    1484
    1485    }
    1486
    1487    /**
    1488     * Resolves a range axis location for a given plot orientation.
    1489     *
    1490     * @param location  the location (<code>null</code> not permitted).
    1491     * @param orientation  the orientation (<code>null</code> not permitted).
    1492     *
    1493     * @return The edge (never <code>null</code>).
    1494     */
    1495    public static RectangleEdge resolveRangeAxisLocation(
    1496            AxisLocation location, PlotOrientation orientation) {
    1497
    1498        if (location == null) {
    1499            throw new IllegalArgumentException("Null 'location' argument.");
    1500        }
    1501        if (orientation == null) {
    1502            throw new IllegalArgumentException("Null 'orientation' argument.");
    1503        }
    1504
    1505        RectangleEdge result = null;
    1506
    1507        if (location == AxisLocation.TOP_OR_RIGHT) {
    1508            if (orientation == PlotOrientation.HORIZONTAL) {
    1509                result = RectangleEdge.TOP;
    1510            }
    1511            else if (orientation == PlotOrientation.VERTICAL) {
    1512                result = RectangleEdge.RIGHT;
    1513            }
    1514        }
    1515        else if (location == AxisLocation.TOP_OR_LEFT) {
    1516            if (orientation == PlotOrientation.HORIZONTAL) {
    1517                result = RectangleEdge.TOP;
    1518            }
    1519            else if (orientation == PlotOrientation.VERTICAL) {
    1520                result = RectangleEdge.LEFT;
    1521            }
    1522        }
    1523        else if (location == AxisLocation.BOTTOM_OR_RIGHT) {
    1524            if (orientation == PlotOrientation.HORIZONTAL) {
    1525                result = RectangleEdge.BOTTOM;
    1526            }
    1527            else if (orientation == PlotOrientation.VERTICAL) {
    1528                result = RectangleEdge.RIGHT;
    1529            }
    1530        }
    1531        else if (location == AxisLocation.BOTTOM_OR_LEFT) {
    1532            if (orientation == PlotOrientation.HORIZONTAL) {
    1533                result = RectangleEdge.BOTTOM;
    1534            }
    1535            else if (orientation == PlotOrientation.VERTICAL) {
    1536                result = RectangleEdge.LEFT;
    1537            }
    1538        }
    1539
    1540        // the above should cover all the options...
    1541        if (result == null) {
    1542            throw new IllegalStateException("resolveRangeAxisLocation()");
    1543        }
    1544        return result;
    1545
    1546    }
    1547
    1548}