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