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