    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
    134    package org.jfree.chart.plot;
    135
    136    import java.awt.AlphaComposite;
    137    import java.awt.BasicStroke;
    138    import java.awt.Color;
    139    import java.awt.Composite;
    140    import java.awt.Font;
    141    import java.awt.GradientPaint;
    142    import java.awt.Graphics2D;
    143    import java.awt.Image;
    144    import java.awt.Paint;
    145    import java.awt.Shape;
    146    import java.awt.Stroke;
    147    import java.awt.geom.Ellipse2D;
    148    import java.awt.geom.Point2D;
    149    import java.awt.geom.Rectangle2D;
    150    import java.io.IOException;
    151    import java.io.ObjectInputStream;
    152    import java.io.ObjectOutputStream;
    153    import java.io.Serializable;
    154
    155    import javax.swing.event.EventListenerList;
    156
    157    import org.jfree.chart.JFreeChart;
    158    import org.jfree.chart.LegendItemCollection;
    159    import org.jfree.chart.LegendItemSource;
    160    import org.jfree.chart.annotations.Annotation;
    161    import org.jfree.chart.axis.AxisLocation;
    162    import org.jfree.chart.entity.EntityCollection;
    163    import org.jfree.chart.entity.PlotEntity;
    164    import org.jfree.chart.event.AnnotationChangeEvent;
    165    import org.jfree.chart.event.AnnotationChangeListener;
    166    import org.jfree.chart.event.AxisChangeEvent;
    167    import org.jfree.chart.event.AxisChangeListener;
    168    import org.jfree.chart.event.ChartChangeEventType;
    169    import org.jfree.chart.event.MarkerChangeEvent;
    170    import org.jfree.chart.event.MarkerChangeListener;
    171    import org.jfree.chart.event.PlotChangeEvent;
    172    import org.jfree.chart.event.PlotChangeListener;
    173    import org.jfree.data.general.DatasetChangeEvent;
    174    import org.jfree.data.general.DatasetChangeListener;
    175    import org.jfree.data.general.DatasetGroup;
    176    import org.jfree.io.SerialUtilities;
    177    import org.jfree.text.G2TextMeasurer;
    178    import org.jfree.text.TextBlock;
    179    import org.jfree.text.TextBlockAnchor;
    180    import org.jfree.text.TextUtilities;
    181    import org.jfree.ui.Align;
    182    import org.jfree.ui.RectangleEdge;
    183    import org.jfree.ui.RectangleInsets;
    184    import org.jfree.util.ObjectUtilities;
    185    import org.jfree.util.PaintUtilities;
    186    import 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     */
    193    public 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    }