Source for org.jfree.chart.plot.Plot

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * ---------
  28:  * Plot.java
  29:  * ---------
  30:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Sylvain Vieujot;
  34:  *                   Jeremy Bowman;
  35:  *                   Andreas Schneider;
  36:  *                   Gideon Krause;
  37:  *                   Nicolas Brodu;
  38:  *                   Michal Krause;
  39:  *                   Richard West, Advanced Micro Devices, Inc.;
  40:  *
  41:  * Changes
  42:  * -------
  43:  * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
  44:  * 18-Sep-2001 : Updated header info and fixed DOS encoding problem (DG);
  45:  * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart
  46:  *               class (DG);
  47:  * 23-Oct-2001 : Created renderer for LinePlot class (DG);
  48:  * 07-Nov-2001 : Changed type names for ChartChangeEvent (DG);
  49:  *               Tidied up some Javadoc comments (DG);
  50:  * 13-Nov-2001 : Changes to allow for null axes on plots such as PiePlot (DG);
  51:  *               Added plot/axis compatibility checks (DG);
  52:  * 12-Dec-2001 : Changed constructors to protected, and removed unnecessary
  53:  *               'throws' clauses (DG);
  54:  * 13-Dec-2001 : Added tooltips (DG);
  55:  * 22-Jan-2002 : Added handleClick() method, as part of implementation for
  56:  *               crosshairs (DG);
  57:  *               Moved tooltips reference into ChartInfo class (DG);
  58:  * 23-Jan-2002 : Added test for null axes in chartChanged() method, thanks
  59:  *               to Barry Evans for the bug report (number 506979 on
  60:  *               SourceForge) (DG);
  61:  *               Added a zoom() method (DG);
  62:  * 05-Feb-2002 : Updated setBackgroundPaint(), setOutlineStroke() and
  63:  *               setOutlinePaint() to better handle null values, as suggested
  64:  *               by Sylvain Vieujot (DG);
  65:  * 06-Feb-2002 : Added background image, plus alpha transparency for background
  66:  *               and foreground (DG);
  67:  * 06-Mar-2002 : Added AxisConstants interface (DG);
  68:  * 26-Mar-2002 : Changed zoom method from empty to abstract (DG);
  69:  * 23-Apr-2002 : Moved dataset from JFreeChart class (DG);
  70:  * 11-May-2002 : Added ShapeFactory interface for getShape() methods,
  71:  *               contributed by Jeremy Bowman (DG);
  72:  * 28-May-2002 : Fixed bug in setSeriesPaint(int, Paint) for subplots (AS);
  73:  * 25-Jun-2002 : Removed redundant imports (DG);
  74:  * 30-Jul-2002 : Added 'no data' message for charts with null or empty
  75:  *               datasets (DG);
  76:  * 21-Aug-2002 : Added code to extend series array if necessary (refer to
  77:  *               SourceForge bug id 594547 for details) (DG);
  78:  * 17-Sep-2002 : Fixed bug in getSeriesOutlineStroke() method, reported by
  79:  *               Andreas Schroeder (DG);
  80:  * 23-Sep-2002 : Added getLegendItems() abstract method (DG);
  81:  * 24-Sep-2002 : Removed firstSeriesIndex, subplots now use their own paint
  82:  *               settings, there is a new mechanism for the legend to collect
  83:  *               the legend items (DG);
  84:  * 27-Sep-2002 : Added dataset group (DG);
  85:  * 14-Oct-2002 : Moved listener storage into EventListenerList.  Changed some
  86:  *               abstract methods to empty implementations (DG);
  87:  * 28-Oct-2002 : Added a getBackgroundImage() method (DG);
  88:  * 21-Nov-2002 : Added a plot index for identifying subplots in combined and
  89:  *               overlaid charts (DG);
  90:  * 22-Nov-2002 : Changed all attributes from 'protected' to 'private'.  Added
  91:  *               dataAreaRatio attribute from David M O'Donnell's code (DG);
  92:  * 09-Jan-2003 : Integrated fix for plot border contributed by Gideon
  93:  *               Krause (DG);
  94:  * 17-Jan-2003 : Moved to com.jrefinery.chart.plot (DG);
  95:  * 23-Jan-2003 : Removed one constructor (DG);
  96:  * 26-Mar-2003 : Implemented Serializable (DG);
  97:  * 14-Jul-2003 : Moved the dataset and secondaryDataset attributes to the
  98:  *               CategoryPlot and XYPlot classes (DG);
  99:  * 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: }