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