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