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