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