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