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