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