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