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