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