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