001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * JFreeChart.java 029 * --------------- 030 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Andrzej Porebski; 034 * David Li; 035 * Wolfgang Irler; 036 * Christian W. Zuckschwerdt; 037 * Klaus Rheinwald; 038 * Nicolas Brodu; 039 * 040 * $Id: JFreeChart.java,v 1.34.2.12 2007/01/17 16:26:29 mungady Exp $ 041 * 042 * Changes (from 20-Jun-2001) 043 * -------------------------- 044 * 20-Jun-2001 : Modifications submitted by Andrzej Porebski for legend 045 * placement; 046 * 21-Jun-2001 : Removed JFreeChart parameter from Plot constructors (DG); 047 * 22-Jun-2001 : Multiple titles added (original code by David Berry, with 048 * reworkings by DG); 049 * 18-Sep-2001 : Updated header (DG); 050 * 15-Oct-2001 : Moved data source classes into new package 051 * com.jrefinery.data.* (DG); 052 * 18-Oct-2001 : New factory method for creating VerticalXYBarChart (DG); 053 * 19-Oct-2001 : Moved series paint and stroke methods to the Plot class (DG); 054 * Moved static chart creation methods to new ChartFactory 055 * class (DG); 056 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); 057 * Fixed bug where chart isn't registered with the dataset (DG); 058 * 07-Nov-2001 : Fixed bug where null title in constructor causes 059 * exception (DG); 060 * Tidied up event notification code (DG); 061 * 17-Nov-2001 : Added getLegendItemCount() method (DG); 062 * 21-Nov-2001 : Set clipping in draw method to ensure that nothing gets drawn 063 * outside the chart area (DG); 064 * 11-Dec-2001 : Added the createBufferedImage() method, taken from the 065 * JFreeChartServletDemo class (DG); 066 * 13-Dec-2001 : Added tooltips (DG); 067 * 16-Jan-2002 : Added handleClick() method (DG); 068 * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG); 069 * 05-Feb-2002 : Removed redundant tooltips code (DG); 070 * 19-Feb-2002 : Added accessor methods for the backgroundImage and 071 * backgroundImageAlpha attributes (DG); 072 * 21-Feb-2002 : Added static fields for INFO, COPYRIGHT, LICENCE, CONTRIBUTORS 073 * and LIBRARIES. These can be used to display information about 074 * JFreeChart (DG); 075 * 06-Mar-2002 : Moved constants to JFreeChartConstants interface (DG); 076 * 18-Apr-2002 : PieDataset is no longer sorted (oldman); 077 * 23-Apr-2002 : Moved dataset to the Plot class (DG); 078 * 13-Jun-2002 : Added an extra draw() method (DG); 079 * 25-Jun-2002 : Implemented the Drawable interface and removed redundant 080 * imports (DG); 081 * 26-Jun-2002 : Added another createBufferedImage() method (DG); 082 * 18-Sep-2002 : Fixed issues reported by Checkstyle (DG); 083 * 23-Sep-2002 : Added new contributor (DG); 084 * 28-Oct-2002 : Created main title and subtitle list to replace existing title 085 * list (DG); 086 * 08-Jan-2003 : Added contributor (DG); 087 * 17-Jan-2003 : Added new constructor (DG); 088 * 22-Jan-2003 : Added ChartColor class by Cameron Riley, and background image 089 * alignment code by Christian W. Zuckschwerdt (DG); 090 * 11-Feb-2003 : Added flag to allow suppression of chart change events, based 091 * on a suggestion by Klaus Rheinwald (DG); 092 * 04-Mar-2003 : Added small fix for suppressed chart change events (see bug id 093 * 690865) (DG); 094 * 10-Mar-2003 : Added Benoit Xhenseval to contributors (DG); 095 * 26-Mar-2003 : Implemented Serializable (DG); 096 * 15-Jul-2003 : Added an optional border for the chart (DG); 097 * 11-Sep-2003 : Took care of listeners while cloning (NB); 098 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 099 * 22-Sep-2003 : Added nullpointer checks. 100 * 25-Sep-2003 : Added nullpointer checks too (NB). 101 * 03-Dec-2003 : Legends are now registered by this class instead of using the 102 * old constructor way (TM); 103 * 03-Dec-2003 : Added anchorPoint to draw() method (DG); 104 * 08-Jan-2004 : Reworked title code, introducing line wrapping (DG); 105 * 09-Feb-2004 : Created additional createBufferedImage() method (DG); 106 * 05-Apr-2004 : Added new createBufferedImage() method (DG); 107 * 27-May-2004 : Moved constants from JFreeChartConstants.java back to this 108 * class (DG); 109 * 25-Nov-2004 : Updates for changes to Title class (DG); 110 * 06-Jan-2005 : Change lookup for default background color (DG); 111 * 31-Jan-2005 : Added Don Elliott to contributors (DG); 112 * 02-Feb-2005 : Added clearSubtitles() method (DG); 113 * 03-Feb-2005 : Added Mofeed Shahin to contributors (DG); 114 * 08-Feb-2005 : Updated for RectangleConstraint changes (DG); 115 * 28-Mar-2005 : Renamed Legend --> OldLegend (DG); 116 * 12-Apr-2005 : Added methods to access legend(s) in subtitle list (DG); 117 * 13-Apr-2005 : Added removeLegend() and removeSubtitle() methods (DG); 118 * 20-Apr-2005 : Modified to collect chart entities from titles and 119 * subtitles (DG); 120 * 26-Apr-2005 : Removed LOGGER (DG); 121 * 06-Jun-2005 : Added addLegend() method and padding attribute, fixed equals() 122 * method (DG); 123 * 24-Nov-2005 : Removed OldLegend and related code - don't want to support 124 * this in 1.0.0 final (DG); 125 * ------------- JFREECHART 1.0.x --------------------------------------------- 126 * 27-Jan-2006 : Updated version number (DG); 127 * 07-Dec-2006 : Added some missing credits (DG); 128 * 17-Jan-2007 : Added Darren Jung to contributor list (DG); 129 * 130 */ 131 132 package org.jfree.chart; 133 134 import java.awt.AlphaComposite; 135 import java.awt.BasicStroke; 136 import java.awt.Color; 137 import java.awt.Composite; 138 import java.awt.Font; 139 import java.awt.Graphics2D; 140 import java.awt.Image; 141 import java.awt.Paint; 142 import java.awt.RenderingHints; 143 import java.awt.Shape; 144 import java.awt.Stroke; 145 import java.awt.geom.AffineTransform; 146 import java.awt.geom.Point2D; 147 import java.awt.geom.Rectangle2D; 148 import java.awt.image.BufferedImage; 149 import java.io.IOException; 150 import java.io.ObjectInputStream; 151 import java.io.ObjectOutputStream; 152 import java.io.Serializable; 153 import java.net.URL; 154 import java.util.ArrayList; 155 import java.util.Arrays; 156 import java.util.Iterator; 157 import java.util.List; 158 import java.util.ResourceBundle; 159 160 import javax.swing.ImageIcon; 161 import javax.swing.UIManager; 162 import javax.swing.event.EventListenerList; 163 164 import org.jfree.JCommon; 165 import org.jfree.chart.block.BlockBorder; 166 import org.jfree.chart.block.BlockParams; 167 import org.jfree.chart.block.EntityBlockResult; 168 import org.jfree.chart.block.LengthConstraintType; 169 import org.jfree.chart.block.RectangleConstraint; 170 import org.jfree.chart.entity.EntityCollection; 171 import org.jfree.chart.event.ChartChangeEvent; 172 import org.jfree.chart.event.ChartChangeListener; 173 import org.jfree.chart.event.ChartProgressEvent; 174 import org.jfree.chart.event.ChartProgressListener; 175 import org.jfree.chart.event.PlotChangeEvent; 176 import org.jfree.chart.event.PlotChangeListener; 177 import org.jfree.chart.event.TitleChangeEvent; 178 import org.jfree.chart.event.TitleChangeListener; 179 import org.jfree.chart.plot.CategoryPlot; 180 import org.jfree.chart.plot.Plot; 181 import org.jfree.chart.plot.PlotRenderingInfo; 182 import org.jfree.chart.plot.XYPlot; 183 import org.jfree.chart.title.LegendTitle; 184 import org.jfree.chart.title.TextTitle; 185 import org.jfree.chart.title.Title; 186 import org.jfree.data.Range; 187 import org.jfree.io.SerialUtilities; 188 import org.jfree.ui.Align; 189 import org.jfree.ui.Drawable; 190 import org.jfree.ui.HorizontalAlignment; 191 import org.jfree.ui.RectangleEdge; 192 import org.jfree.ui.RectangleInsets; 193 import org.jfree.ui.Size2D; 194 import org.jfree.ui.VerticalAlignment; 195 import org.jfree.ui.about.Contributor; 196 import org.jfree.ui.about.Licences; 197 import org.jfree.ui.about.ProjectInfo; 198 import org.jfree.util.ObjectUtilities; 199 import org.jfree.util.PaintUtilities; 200 201 /** 202 * A chart class implemented using the Java 2D APIs. The current version 203 * supports bar charts, line charts, pie charts and xy plots (including time 204 * series data). 205 * <P> 206 * JFreeChart coordinates several objects to achieve its aim of being able to 207 * draw a chart on a Java 2D graphics device: a list of {@link Title} objects 208 * (which often includes the chart's legend), a {@link Plot} and a 209 * {@link org.jfree.data.general.Dataset} (the plot in turn manages a 210 * domain axis and a range axis). 211 * <P> 212 * You should use a {@link ChartPanel} to display a chart in a GUI. 213 * <P> 214 * The {@link ChartFactory} class contains static methods for creating 215 * 'ready-made' charts. 216 * 217 * @see ChartPanel 218 * @see ChartFactory 219 * @see Title 220 * @see Plot 221 */ 222 public class JFreeChart implements Drawable, 223 TitleChangeListener, 224 PlotChangeListener, 225 Serializable, 226 Cloneable { 227 228 /** For serialization. */ 229 private static final long serialVersionUID = -3470703747817429120L; 230 231 /** Information about the project. */ 232 public static final ProjectInfo INFO = new JFreeChartInfo(); 233 234 /** The default font for titles. */ 235 public static final Font DEFAULT_TITLE_FONT 236 = new Font("SansSerif", Font.BOLD, 18); 237 238 /** The default background color. */ 239 public static final Paint DEFAULT_BACKGROUND_PAINT 240 = UIManager.getColor("Panel.background"); 241 242 /** The default background image. */ 243 public static final Image DEFAULT_BACKGROUND_IMAGE = null; 244 245 /** The default background image alignment. */ 246 public static final int DEFAULT_BACKGROUND_IMAGE_ALIGNMENT = Align.FIT; 247 248 /** The default background image alpha. */ 249 public static final float DEFAULT_BACKGROUND_IMAGE_ALPHA = 0.5f; 250 251 /** 252 * Rendering hints that will be used for chart drawing. This should never 253 * be <code>null</code>. 254 */ 255 private transient RenderingHints renderingHints; 256 257 /** A flag that controls whether or not the chart border is drawn. */ 258 private boolean borderVisible; 259 260 /** The stroke used to draw the chart border (if visible). */ 261 private transient Stroke borderStroke; 262 263 /** The paint used to draw the chart border (if visible). */ 264 private transient Paint borderPaint; 265 266 /** The padding between the chart border and the chart drawing area. */ 267 private RectangleInsets padding; 268 269 /** The chart title (optional). */ 270 private TextTitle title; 271 272 /** The chart subtitles (zero, one or many). */ 273 private List subtitles; 274 275 /** Draws the visual representation of the data. */ 276 private Plot plot; 277 278 /** Paint used to draw the background of the chart. */ 279 private transient Paint backgroundPaint; 280 281 /** An optional background image for the chart. */ 282 private transient Image backgroundImage; // todo: not serialized yet 283 284 /** The alignment for the background image. */ 285 private int backgroundImageAlignment = Align.FIT; 286 287 /** The alpha transparency for the background image. */ 288 private float backgroundImageAlpha = 0.5f; 289 290 /** Storage for registered change listeners. */ 291 private transient EventListenerList changeListeners; 292 293 /** Storage for registered progress listeners. */ 294 private transient EventListenerList progressListeners; 295 296 /** 297 * A flag that can be used to enable/disable notification of chart change 298 * events. 299 */ 300 private boolean notify; 301 302 /** 303 * Creates a new chart based on the supplied plot. The chart will have 304 * a legend added automatically, but no title (although you can easily add 305 * one later). 306 * <br><br> 307 * Note that the {@link ChartFactory} class contains a range 308 * of static methods that will return ready-made charts, and often this 309 * is a more convenient way to create charts than using this constructor. 310 * 311 * @param plot the plot (<code>null</code> not permitted). 312 */ 313 public JFreeChart(Plot plot) { 314 this(null, null, plot, true); 315 } 316 317 /** 318 * Creates a new chart with the given title and plot. A default font 319 * (@link DEFAULT_TITLE_FONT) is used for the title, and the chart will 320 * have a legend added automatically. 321 * <br><br> 322 * Note that the {@link ChartFactory} class contains a range 323 * of static methods that will return ready-made charts, and often this 324 * is a more convenient way to create charts than using this constructor. 325 * 326 * @param title the chart title (<code>null</code> permitted). 327 * @param plot the plot (<code>null</code> not permitted). 328 */ 329 public JFreeChart(String title, Plot plot) { 330 this(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true); 331 } 332 333 /** 334 * Creates a new chart with the given title and plot. The 335 * <code>createLegend</code> argument specifies whether or not a legend 336 * should be added to the chart. 337 * <br><br> 338 * Note that the {@link ChartFactory} class contains a range 339 * of static methods that will return ready-made charts, and often this 340 * is a more convenient way to create charts than using this constructor. 341 * 342 * @param title the chart title (<code>null</code> permitted). 343 * @param titleFont the font for displaying the chart title 344 * (<code>null</code> permitted). 345 * @param plot controller of the visual representation of the data 346 * (<code>null</code> not permitted). 347 * @param createLegend a flag indicating whether or not a legend should 348 * be created for the chart. 349 */ 350 public JFreeChart(String title, Font titleFont, Plot plot, 351 boolean createLegend) { 352 353 if (plot == null) { 354 throw new NullPointerException("Null 'plot' argument."); 355 } 356 357 // create storage for listeners... 358 this.progressListeners = new EventListenerList(); 359 this.changeListeners = new EventListenerList(); 360 this.notify = true; // default is to notify listeners when the 361 // chart changes 362 363 this.renderingHints = new RenderingHints( 364 RenderingHints.KEY_ANTIALIASING, 365 RenderingHints.VALUE_ANTIALIAS_ON); 366 367 this.borderVisible = false; 368 this.borderStroke = new BasicStroke(1.0f); 369 this.borderPaint = Color.black; 370 371 this.padding = RectangleInsets.ZERO_INSETS; 372 373 this.plot = plot; 374 plot.addChangeListener(this); 375 376 this.subtitles = new ArrayList(); 377 378 // create a legend, if requested... 379 if (createLegend) { 380 LegendTitle legend = new LegendTitle(this.plot); 381 legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); 382 legend.setBorder(new BlockBorder()); 383 legend.setBackgroundPaint(Color.white); 384 legend.setPosition(RectangleEdge.BOTTOM); 385 this.subtitles.add(legend); 386 } 387 388 // add the chart title, if one has been specified... 389 if (title != null) { 390 if (titleFont == null) { 391 titleFont = DEFAULT_TITLE_FONT; 392 } 393 this.title = new TextTitle(title, titleFont); 394 this.title.addChangeListener(this); 395 } 396 397 this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; 398 399 this.backgroundImage = DEFAULT_BACKGROUND_IMAGE; 400 this.backgroundImageAlignment = DEFAULT_BACKGROUND_IMAGE_ALIGNMENT; 401 this.backgroundImageAlpha = DEFAULT_BACKGROUND_IMAGE_ALPHA; 402 403 } 404 405 /** 406 * Returns the collection of rendering hints for the chart. 407 * 408 * @return The rendering hints for the chart (never <code>null</code>). 409 */ 410 public RenderingHints getRenderingHints() { 411 return this.renderingHints; 412 } 413 414 /** 415 * Sets the rendering hints for the chart. These will be added (using the 416 * Graphics2D.addRenderingHints() method) near the start of the 417 * JFreeChart.draw() method. 418 * 419 * @param renderingHints the rendering hints (<code>null</code> not 420 * permitted). 421 */ 422 public void setRenderingHints(RenderingHints renderingHints) { 423 if (renderingHints == null) { 424 throw new NullPointerException("RenderingHints given are null"); 425 } 426 this.renderingHints = renderingHints; 427 fireChartChanged(); 428 } 429 430 /** 431 * Returns a flag that controls whether or not a border is drawn around the 432 * outside of the chart. 433 * 434 * @return A boolean. 435 */ 436 public boolean isBorderVisible() { 437 return this.borderVisible; 438 } 439 440 /** 441 * Sets a flag that controls whether or not a border is drawn around the 442 * outside of the chart. 443 * 444 * @param visible the flag. 445 */ 446 public void setBorderVisible(boolean visible) { 447 this.borderVisible = visible; 448 fireChartChanged(); 449 } 450 451 /** 452 * Returns the stroke used to draw the chart border (if visible). 453 * 454 * @return The border stroke. 455 */ 456 public Stroke getBorderStroke() { 457 return this.borderStroke; 458 } 459 460 /** 461 * Sets the stroke used to draw the chart border (if visible). 462 * 463 * @param stroke the stroke. 464 */ 465 public void setBorderStroke(Stroke stroke) { 466 this.borderStroke = stroke; 467 fireChartChanged(); 468 } 469 470 /** 471 * Returns the paint used to draw the chart border (if visible). 472 * 473 * @return The border paint. 474 */ 475 public Paint getBorderPaint() { 476 return this.borderPaint; 477 } 478 479 /** 480 * Sets the paint used to draw the chart border (if visible). 481 * 482 * @param paint the paint. 483 */ 484 public void setBorderPaint(Paint paint) { 485 this.borderPaint = paint; 486 fireChartChanged(); 487 } 488 489 /** 490 * Returns the padding between the chart border and the chart drawing area. 491 * 492 * @return The padding (never <code>null</code>). 493 */ 494 public RectangleInsets getPadding() { 495 return this.padding; 496 } 497 498 /** 499 * Sets the padding between the chart border and the chart drawing area, 500 * and sends a {@link ChartChangeEvent} to all registered listeners. 501 * 502 * @param padding the padding (<code>null</code> not permitted). 503 */ 504 public void setPadding(RectangleInsets padding) { 505 if (padding == null) { 506 throw new IllegalArgumentException("Null 'padding' argument."); 507 } 508 this.padding = padding; 509 notifyListeners(new ChartChangeEvent(this)); 510 } 511 512 /** 513 * Returns the main chart title. Very often a chart will have just one 514 * title, so we make this case simple by providing accessor methods for 515 * the main title. However, multiple titles are supported - see the 516 * {@link #addSubtitle(Title)} method. 517 * 518 * @return The chart title (possibly <code>null</code>). 519 */ 520 public TextTitle getTitle() { 521 return this.title; 522 } 523 524 /** 525 * Sets the main title for the chart and sends a {@link ChartChangeEvent} 526 * to all registered listeners. If you do not want a title for the 527 * chart, set it to <code>null</code>. If you want more than one title on 528 * a chart, use the {@link #addSubtitle(Title)} method. 529 * 530 * @param title the title (<code>null</code> permitted). 531 */ 532 public void setTitle(TextTitle title) { 533 this.title = title; 534 fireChartChanged(); 535 } 536 537 /** 538 * Sets the chart title and sends a {@link ChartChangeEvent} to all 539 * registered listeners. This is a convenience method that ends up calling 540 * the {@link #setTitle(TextTitle)} method. If there is an existing title, 541 * its text is updated, otherwise a new title using the default font is 542 * added to the chart. If <code>text</code> is <code>null</code> the chart 543 * title is set to <code>null</code>. 544 * 545 * @param text the title text (<code>null</code> permitted). 546 */ 547 public void setTitle(String text) { 548 if (text != null) { 549 if (this.title == null) { 550 setTitle(new TextTitle(text, JFreeChart.DEFAULT_TITLE_FONT)); 551 } 552 else { 553 this.title.setText(text); 554 } 555 } 556 else { 557 setTitle((TextTitle) null); 558 } 559 } 560 561 /** 562 * Adds a legend to the plot and sends a {@link ChartChangeEvent} to all 563 * registered listeners. 564 * 565 * @param legend the legend (<code>null</code> not permitted). 566 */ 567 public void addLegend(LegendTitle legend) { 568 addSubtitle(legend); 569 } 570 571 /** 572 * Returns the legend for the chart, if there is one. Note that a chart 573 * can have more than one legend - this method returns the first. 574 * 575 * @return The legend (possibly <code>null</code>). 576 */ 577 public LegendTitle getLegend() { 578 return getLegend(0); 579 } 580 581 /** 582 * Returns the nth legend for a chart, or <code>null</code>. 583 * 584 * @param index the legend index (zero-based). 585 * 586 * @return The legend (possibly <code>null</code>). 587 */ 588 public LegendTitle getLegend(int index) { 589 int seen = 0; 590 Iterator iterator = this.subtitles.iterator(); 591 while (iterator.hasNext()) { 592 Title subtitle = (Title) iterator.next(); 593 if (subtitle instanceof LegendTitle) { 594 if (seen == index) { 595 return (LegendTitle) subtitle; 596 } 597 else { 598 seen++; 599 } 600 } 601 } 602 return null; 603 } 604 605 /** 606 * Removes the first legend in the chart and sends a 607 * {@link ChartChangeEvent} to all registered listeners. 608 */ 609 public void removeLegend() { 610 removeSubtitle(getLegend()); 611 } 612 613 /** 614 * Returns the list of subtitles for the chart. 615 * 616 * @return The subtitle list (possibly empty, but never <code>null</code>). 617 */ 618 public List getSubtitles() { 619 return this.subtitles; 620 } 621 622 /** 623 * Sets the title list for the chart (completely replaces any existing 624 * titles). 625 * 626 * @param subtitles the new list of subtitles (<code>null</code> not 627 * permitted). 628 */ 629 public void setSubtitles(List subtitles) { 630 if (subtitles == null) { 631 throw new NullPointerException("Null 'subtitles' argument."); 632 } 633 this.subtitles = subtitles; 634 fireChartChanged(); 635 } 636 637 /** 638 * Returns the number of titles for the chart. 639 * 640 * @return The number of titles for the chart. 641 */ 642 public int getSubtitleCount() { 643 return this.subtitles.size(); 644 } 645 646 /** 647 * Returns a chart subtitle. 648 * 649 * @param index the index of the chart subtitle (zero based). 650 * 651 * @return A chart subtitle. 652 */ 653 public Title getSubtitle(int index) { 654 if ((index < 0) || (index == getSubtitleCount())) { 655 throw new IllegalArgumentException("Index out of range."); 656 } 657 return (Title) this.subtitles.get(index); 658 } 659 660 /** 661 * Adds a chart subtitle, and notifies registered listeners that the chart 662 * has been modified. 663 * 664 * @param subtitle the subtitle (<code>null</code> not permitted). 665 */ 666 public void addSubtitle(Title subtitle) { 667 if (subtitle == null) { 668 throw new IllegalArgumentException("Null 'subtitle' argument."); 669 } 670 this.subtitles.add(subtitle); 671 subtitle.addChangeListener(this); 672 fireChartChanged(); 673 } 674 675 /** 676 * Clears all subtitles from the chart and sends a {@link ChartChangeEvent} 677 * to all registered listeners. 678 */ 679 public void clearSubtitles() { 680 Iterator iterator = this.subtitles.iterator(); 681 while (iterator.hasNext()) { 682 Title t = (Title) iterator.next(); 683 t.removeChangeListener(this); 684 } 685 this.subtitles.clear(); 686 fireChartChanged(); 687 } 688 689 /** 690 * Removes the specified subtitle and sends a {@link ChartChangeEvent} to 691 * all registered listeners. 692 * 693 * @param title the title. 694 */ 695 public void removeSubtitle(Title title) { 696 this.subtitles.remove(title); 697 fireChartChanged(); 698 } 699 700 /** 701 * Returns the plot for the chart. The plot is a class responsible for 702 * coordinating the visual representation of the data, including the axes 703 * (if any). 704 * 705 * @return The plot. 706 */ 707 public Plot getPlot() { 708 return this.plot; 709 } 710 711 /** 712 * Returns the plot cast as a {@link CategoryPlot}. 713 * <p> 714 * NOTE: if the plot is not an instance of {@link CategoryPlot}, then a 715 * <code>ClassCastException</code> is thrown. 716 * 717 * @return The plot. 718 */ 719 public CategoryPlot getCategoryPlot() { 720 return (CategoryPlot) this.plot; 721 } 722 723 /** 724 * Returns the plot cast as an {@link XYPlot}. 725 * <p> 726 * NOTE: if the plot is not an instance of {@link XYPlot}, then a 727 * <code>ClassCastException</code> is thrown. 728 * 729 * @return The plot. 730 */ 731 public XYPlot getXYPlot() { 732 return (XYPlot) this.plot; 733 } 734 735 /** 736 * Returns a flag that indicates whether or not anti-aliasing is used when 737 * the chart is drawn. 738 * 739 * @return The flag. 740 */ 741 public boolean getAntiAlias() { 742 Object o = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING); 743 if (o == null) { 744 return false; 745 } 746 return (o.equals(RenderingHints.VALUE_ANTIALIAS_ON)); 747 } 748 749 /** 750 * Sets a flag that indicates whether or not anti-aliasing is used when the 751 * chart is drawn. 752 * <P> 753 * Anti-aliasing usually improves the appearance of charts, but is slower. 754 * 755 * @param flag the new value of the flag. 756 */ 757 public void setAntiAlias(boolean flag) { 758 759 Object o = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING); 760 if (o == null) { 761 o = RenderingHints.VALUE_ANTIALIAS_DEFAULT; 762 } 763 if (!flag && RenderingHints.VALUE_ANTIALIAS_OFF.equals(o) 764 || flag && RenderingHints.VALUE_ANTIALIAS_ON.equals(o)) { 765 // no change, do nothing 766 return; 767 } 768 if (flag) { 769 this.renderingHints.put(RenderingHints.KEY_ANTIALIASING, 770 RenderingHints.VALUE_ANTIALIAS_ON); 771 } 772 else { 773 this.renderingHints.put(RenderingHints.KEY_ANTIALIASING, 774 RenderingHints.VALUE_ANTIALIAS_OFF); 775 } 776 fireChartChanged(); 777 778 } 779 780 /** 781 * Returns the paint used for the chart background. 782 * 783 * @return The paint (possibly <code>null</code>). 784 */ 785 public Paint getBackgroundPaint() { 786 return this.backgroundPaint; 787 } 788 789 /** 790 * Sets the paint used to fill the chart background and sends a 791 * {@link ChartChangeEvent} to all registered listeners. 792 * 793 * @param paint the paint (<code>null</code> permitted). 794 */ 795 public void setBackgroundPaint(Paint paint) { 796 797 if (this.backgroundPaint != null) { 798 if (!this.backgroundPaint.equals(paint)) { 799 this.backgroundPaint = paint; 800 fireChartChanged(); 801 } 802 } 803 else { 804 if (paint != null) { 805 this.backgroundPaint = paint; 806 fireChartChanged(); 807 } 808 } 809 810 } 811 812 /** 813 * Returns the background image for the chart, or <code>null</code> if 814 * there is no image. 815 * 816 * @return The image (possibly <code>null</code>). 817 */ 818 public Image getBackgroundImage() { 819 return this.backgroundImage; 820 } 821 822 /** 823 * Sets the background image for the chart and sends a 824 * {@link ChartChangeEvent} to all registered listeners. 825 * 826 * @param image the image (<code>null</code> permitted). 827 */ 828 public void setBackgroundImage(Image image) { 829 830 if (this.backgroundImage != null) { 831 if (!this.backgroundImage.equals(image)) { 832 this.backgroundImage = image; 833 fireChartChanged(); 834 } 835 } 836 else { 837 if (image != null) { 838 this.backgroundImage = image; 839 fireChartChanged(); 840 } 841 } 842 843 } 844 845 /** 846 * Returns the background image alignment. Alignment constants are defined 847 * in the <code>org.jfree.ui.Align</code> class in the JCommon class 848 * library. 849 * 850 * @return The alignment. 851 */ 852 public int getBackgroundImageAlignment() { 853 return this.backgroundImageAlignment; 854 } 855 856 /** 857 * Sets the background alignment. Alignment options are defined by the 858 * {@link org.jfree.ui.Align} class. 859 * 860 * @param alignment the alignment. 861 */ 862 public void setBackgroundImageAlignment(int alignment) { 863 if (this.backgroundImageAlignment != alignment) { 864 this.backgroundImageAlignment = alignment; 865 fireChartChanged(); 866 } 867 } 868 869 /** 870 * Returns the alpha-transparency for the chart's background image. 871 * 872 * @return The alpha-transparency. 873 */ 874 public float getBackgroundImageAlpha() { 875 return this.backgroundImageAlpha; 876 } 877 878 /** 879 * Sets the alpha-transparency for the chart's background image. 880 * Registered listeners are notified that the chart has been changed. 881 * 882 * @param alpha the alpha value. 883 */ 884 public void setBackgroundImageAlpha(float alpha) { 885 886 if (this.backgroundImageAlpha != alpha) { 887 this.backgroundImageAlpha = alpha; 888 fireChartChanged(); 889 } 890 891 } 892 893 /** 894 * Returns a flag that controls whether or not change events are sent to 895 * registered listeners. 896 * 897 * @return A boolean. 898 */ 899 public boolean isNotify() { 900 return this.notify; 901 } 902 903 /** 904 * Sets a flag that controls whether or not listeners receive 905 * {@link ChartChangeEvent} notifications. 906 * 907 * @param notify a boolean. 908 */ 909 public void setNotify(boolean notify) { 910 this.notify = notify; 911 // if the flag is being set to true, there may be queued up changes... 912 if (notify) { 913 notifyListeners(new ChartChangeEvent(this)); 914 } 915 } 916 917 /** 918 * Draws the chart on a Java 2D graphics device (such as the screen or a 919 * printer). 920 * <P> 921 * This method is the focus of the entire JFreeChart library. 922 * 923 * @param g2 the graphics device. 924 * @param area the area within which the chart should be drawn. 925 */ 926 public void draw(Graphics2D g2, Rectangle2D area) { 927 draw(g2, area, null, null); 928 } 929 930 /** 931 * Draws the chart on a Java 2D graphics device (such as the screen or a 932 * printer). This method is the focus of the entire JFreeChart library. 933 * 934 * @param g2 the graphics device. 935 * @param area the area within which the chart should be drawn. 936 * @param info records info about the drawing (null means collect no info). 937 */ 938 public void draw(Graphics2D g2, Rectangle2D area, ChartRenderingInfo info) { 939 draw(g2, area, null, info); 940 } 941 942 /** 943 * Draws the chart on a Java 2D graphics device (such as the screen or a 944 * printer). 945 * <P> 946 * This method is the focus of the entire JFreeChart library. 947 * 948 * @param g2 the graphics device. 949 * @param chartArea the area within which the chart should be drawn. 950 * @param anchor the anchor point (in Java2D space) for the chart 951 * (<code>null</code> permitted). 952 * @param info records info about the drawing (null means collect no info). 953 */ 954 public void draw(Graphics2D g2, 955 Rectangle2D chartArea, Point2D anchor, 956 ChartRenderingInfo info) { 957 958 notifyListeners(new ChartProgressEvent(this, this, 959 ChartProgressEvent.DRAWING_STARTED, 0)); 960 961 // record the chart area, if info is requested... 962 if (info != null) { 963 info.clear(); 964 info.setChartArea(chartArea); 965 } 966 967 // ensure no drawing occurs outside chart area... 968 Shape savedClip = g2.getClip(); 969 g2.clip(chartArea); 970 971 g2.addRenderingHints(this.renderingHints); 972 973 // draw the chart background... 974 if (this.backgroundPaint != null) { 975 g2.setPaint(this.backgroundPaint); 976 g2.fill(chartArea); 977 } 978 979 if (this.backgroundImage != null) { 980 Composite originalComposite = g2.getComposite(); 981 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 982 this.backgroundImageAlpha)); 983 Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, 984 this.backgroundImage.getWidth(null), 985 this.backgroundImage.getHeight(null)); 986 Align.align(dest, chartArea, this.backgroundImageAlignment); 987 g2.drawImage(this.backgroundImage, (int) dest.getX(), 988 (int) dest.getY(), (int) dest.getWidth(), 989 (int) dest.getHeight(), null); 990 g2.setComposite(originalComposite); 991 } 992 993 if (isBorderVisible()) { 994 Paint paint = getBorderPaint(); 995 Stroke stroke = getBorderStroke(); 996 if (paint != null && stroke != null) { 997 Rectangle2D borderArea = new Rectangle2D.Double( 998 chartArea.getX(), chartArea.getY(), 999 chartArea.getWidth() - 1.0, chartArea.getHeight() 1000 - 1.0); 1001 g2.setPaint(paint); 1002 g2.setStroke(stroke); 1003 g2.draw(borderArea); 1004 } 1005 } 1006 1007 // draw the title and subtitles... 1008 Rectangle2D nonTitleArea = new Rectangle2D.Double(); 1009 nonTitleArea.setRect(chartArea); 1010 this.padding.trim(nonTitleArea); 1011 1012 EntityCollection entities = null; 1013 if (info != null) { 1014 entities = info.getEntityCollection(); 1015 } 1016 if (this.title != null) { 1017 EntityCollection e = drawTitle(this.title, g2, nonTitleArea, 1018 (entities != null)); 1019 if (e != null) { 1020 entities.addAll(e); 1021 } 1022 } 1023 1024 Iterator iterator = this.subtitles.iterator(); 1025 while (iterator.hasNext()) { 1026 Title currentTitle = (Title) iterator.next(); 1027 EntityCollection e = drawTitle(currentTitle, g2, nonTitleArea, 1028 (entities != null)); 1029 if (e != null) { 1030 entities.addAll(e); 1031 } 1032 } 1033 1034 Rectangle2D plotArea = nonTitleArea; 1035 1036 // draw the plot (axes and data visualisation) 1037 PlotRenderingInfo plotInfo = null; 1038 if (info != null) { 1039 plotInfo = info.getPlotInfo(); 1040 } 1041 this.plot.draw(g2, plotArea, anchor, null, plotInfo); 1042 1043 g2.setClip(savedClip); 1044 1045 notifyListeners(new ChartProgressEvent(this, this, 1046 ChartProgressEvent.DRAWING_FINISHED, 100)); 1047 } 1048 1049 /** 1050 * Creates a rectangle that is aligned to the frame. 1051 * 1052 * @param dimensions 1053 * @param frame 1054 * @param hAlign 1055 * @param vAlign 1056 * 1057 * @return A rectangle. 1058 */ 1059 private Rectangle2D createAlignedRectangle2D(Size2D dimensions, 1060 Rectangle2D frame, HorizontalAlignment hAlign, 1061 VerticalAlignment vAlign) { 1062 double x = Double.NaN; 1063 double y = Double.NaN; 1064 if (hAlign == HorizontalAlignment.LEFT) { 1065 x = frame.getX(); 1066 } 1067 else if (hAlign == HorizontalAlignment.CENTER) { 1068 x = frame.getCenterX() - (dimensions.width / 2.0); 1069 } 1070 else if (hAlign == HorizontalAlignment.RIGHT) { 1071 x = frame.getMaxX() - dimensions.width; 1072 } 1073 if (vAlign == VerticalAlignment.TOP) { 1074 y = frame.getY(); 1075 } 1076 else if (vAlign == VerticalAlignment.CENTER) { 1077 y = frame.getCenterY() - (dimensions.height / 2.0); 1078 } 1079 else if (vAlign == VerticalAlignment.BOTTOM) { 1080 y = frame.getMaxY() - dimensions.height; 1081 } 1082 1083 return new Rectangle2D.Double(x, y, dimensions.width, 1084 dimensions.height); 1085 } 1086 1087 /** 1088 * Draws a title. The title should be drawn at the top, bottom, left or 1089 * right of the specified area, and the area should be updated to reflect 1090 * the amount of space used by the title. 1091 * 1092 * @param t the title (<code>null</code> not permitted). 1093 * @param g2 the graphics device (<code>null</code> not permitted). 1094 * @param area the chart area, excluding any existing titles 1095 * (<code>null</code> not permitted). 1096 * @param entities a flag that controls whether or not an entity 1097 * collection is returned for the title. 1098 * 1099 * @return An entity collection for the title (possibly <code>null</code>). 1100 */ 1101 protected EntityCollection drawTitle(Title t, Graphics2D g2, 1102 Rectangle2D area, boolean entities) { 1103 1104 if (t == null) { 1105 throw new IllegalArgumentException("Null 't' argument."); 1106 } 1107 if (area == null) { 1108 throw new IllegalArgumentException("Null 'area' argument."); 1109 } 1110 Rectangle2D titleArea = new Rectangle2D.Double(); 1111 RectangleEdge position = t.getPosition(); 1112 double ww = area.getWidth(); 1113 if (ww <= 0.0) { 1114 return null; 1115 } 1116 double hh = area.getHeight(); 1117 if (hh <= 0.0) { 1118 return null; 1119 } 1120 RectangleConstraint constraint = new RectangleConstraint(ww, 1121 new Range(0.0, ww), LengthConstraintType.RANGE, hh, 1122 new Range(0.0, hh), LengthConstraintType.RANGE); 1123 Object retValue = null; 1124 BlockParams p = new BlockParams(); 1125 p.setGenerateEntities(entities); 1126 if (position == RectangleEdge.TOP) { 1127 Size2D size = t.arrange(g2, constraint); 1128 titleArea = createAlignedRectangle2D(size, area, 1129 t.getHorizontalAlignment(), VerticalAlignment.TOP); 1130 retValue = t.draw(g2, titleArea, p); 1131 area.setRect(area.getX(), Math.min(area.getY() + size.height, 1132 area.getMaxY()), area.getWidth(), Math.max(area.getHeight() 1133 - size.height, 0)); 1134 } 1135 else if (position == RectangleEdge.BOTTOM) { 1136 Size2D size = t.arrange(g2, constraint); 1137 titleArea = createAlignedRectangle2D(size, area, 1138 t.getHorizontalAlignment(), VerticalAlignment.BOTTOM); 1139 retValue = t.draw(g2, titleArea, p); 1140 area.setRect(area.getX(), area.getY(), area.getWidth(), 1141 area.getHeight() - size.height); 1142 } 1143 else if (position == RectangleEdge.RIGHT) { 1144 Size2D size = t.arrange(g2, constraint); 1145 titleArea = createAlignedRectangle2D(size, area, 1146 HorizontalAlignment.RIGHT, t.getVerticalAlignment()); 1147 retValue = t.draw(g2, titleArea, p); 1148 area.setRect(area.getX(), area.getY(), area.getWidth() 1149 - size.width, area.getHeight()); 1150 } 1151 1152 else if (position == RectangleEdge.LEFT) { 1153 Size2D size = t.arrange(g2, constraint); 1154 titleArea = createAlignedRectangle2D(size, area, 1155 HorizontalAlignment.LEFT, t.getVerticalAlignment()); 1156 retValue = t.draw(g2, titleArea, p); 1157 area.setRect(area.getX() + size.width, area.getY(), area.getWidth() 1158 - size.width, area.getHeight()); 1159 } 1160 else { 1161 throw new RuntimeException("Unrecognised title position."); 1162 } 1163 EntityCollection result = null; 1164 if (retValue instanceof EntityBlockResult) { 1165 EntityBlockResult ebr = (EntityBlockResult) retValue; 1166 result = ebr.getEntityCollection(); 1167 } 1168 return result; 1169 } 1170 1171 /** 1172 * Creates and returns a buffered image into which the chart has been drawn. 1173 * 1174 * @param width the width. 1175 * @param height the height. 1176 * 1177 * @return A buffered image. 1178 */ 1179 public BufferedImage createBufferedImage(int width, int height) { 1180 return createBufferedImage(width, height, null); 1181 } 1182 1183 /** 1184 * Creates and returns a buffered image into which the chart has been drawn. 1185 * 1186 * @param width the width. 1187 * @param height the height. 1188 * @param info carries back chart state information (<code>null</code> 1189 * permitted). 1190 * 1191 * @return A buffered image. 1192 */ 1193 public BufferedImage createBufferedImage(int width, int height, 1194 ChartRenderingInfo info) { 1195 return createBufferedImage(width, height, BufferedImage.TYPE_INT_RGB, 1196 info); 1197 } 1198 1199 /** 1200 * Creates and returns a buffered image into which the chart has been drawn. 1201 * 1202 * @param width the width. 1203 * @param height the height. 1204 * @param imageType the image type. 1205 * @param info carries back chart state information (<code>null</code> 1206 * permitted). 1207 * 1208 * @return A buffered image. 1209 */ 1210 public BufferedImage createBufferedImage(int width, int height, 1211 int imageType, 1212 ChartRenderingInfo info) { 1213 BufferedImage image = new BufferedImage(width, height, imageType); 1214 Graphics2D g2 = image.createGraphics(); 1215 draw(g2, new Rectangle2D.Double(0, 0, width, height), null, info); 1216 g2.dispose(); 1217 return image; 1218 } 1219 1220 /** 1221 * Creates and returns a buffered image into which the chart has been drawn. 1222 * 1223 * @param imageWidth the image width. 1224 * @param imageHeight the image height. 1225 * @param drawWidth the width for drawing the chart (will be scaled to 1226 * fit image). 1227 * @param drawHeight the height for drawing the chart (will be scaled to 1228 * fit image). 1229 * @param info optional object for collection chart dimension and entity 1230 * information. 1231 * 1232 * @return A buffered image. 1233 */ 1234 public BufferedImage createBufferedImage(int imageWidth, 1235 int imageHeight, 1236 double drawWidth, 1237 double drawHeight, 1238 ChartRenderingInfo info) { 1239 1240 BufferedImage image = new BufferedImage(imageWidth, imageHeight, 1241 BufferedImage.TYPE_INT_RGB); 1242 Graphics2D g2 = image.createGraphics(); 1243 double scaleX = imageWidth / drawWidth; 1244 double scaleY = imageHeight / drawHeight; 1245 AffineTransform st = AffineTransform.getScaleInstance(scaleX, scaleY); 1246 g2.transform(st); 1247 draw(g2, new Rectangle2D.Double(0, 0, drawWidth, drawHeight), null, 1248 info); 1249 g2.dispose(); 1250 return image; 1251 1252 } 1253 1254 /** 1255 * Handles a 'click' on the chart. 1256 * <P> 1257 * JFreeChart is not a UI component, so some other object (e.g. ChartPanel) 1258 * needs to capture the click event and pass it onto the JFreeChart object. 1259 * If you are not using JFreeChart in a client application, then this 1260 * method is not required (and hopefully it doesn't get in the way). 1261 * 1262 * @param x x-coordinate of the click (in Java2D space). 1263 * @param y y-coordinate of the click (in Java2D space). 1264 * @param info contains chart dimension and entity information. 1265 */ 1266 public void handleClick(int x, int y, ChartRenderingInfo info) { 1267 1268 // pass the click on to the plot... 1269 // rely on the plot to post a plot change event and redraw the chart... 1270 this.plot.handleClick(x, y, info.getPlotInfo()); 1271 1272 } 1273 1274 /** 1275 * Registers an object for notification of changes to the chart. 1276 * 1277 * @param listener the listener (<code>null</code> not permitted). 1278 */ 1279 public void addChangeListener(ChartChangeListener listener) { 1280 if (listener == null) { 1281 throw new IllegalArgumentException("Null 'listener' argument."); 1282 } 1283 this.changeListeners.add(ChartChangeListener.class, listener); 1284 } 1285 1286 /** 1287 * Deregisters an object for notification of changes to the chart. 1288 * 1289 * @param listener the listener (<code>null</code> not permitted) 1290 */ 1291 public void removeChangeListener(ChartChangeListener listener) { 1292 if (listener == null) { 1293 throw new IllegalArgumentException("Null 'listener' argument."); 1294 } 1295 this.changeListeners.remove(ChartChangeListener.class, listener); 1296 } 1297 1298 /** 1299 * Sends a default {@link ChartChangeEvent} to all registered listeners. 1300 * <P> 1301 * This method is for convenience only. 1302 */ 1303 public void fireChartChanged() { 1304 ChartChangeEvent event = new ChartChangeEvent(this); 1305 notifyListeners(event); 1306 } 1307 1308 /** 1309 * Sends a {@link ChartChangeEvent} to all registered listeners. 1310 * 1311 * @param event information about the event that triggered the 1312 * notification. 1313 */ 1314 protected void notifyListeners(ChartChangeEvent event) { 1315 if (this.notify) { 1316 Object[] listeners = this.changeListeners.getListenerList(); 1317 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1318 if (listeners[i] == ChartChangeListener.class) { 1319 ((ChartChangeListener) listeners[i + 1]).chartChanged( 1320 event); 1321 } 1322 } 1323 } 1324 } 1325 1326 /** 1327 * Registers an object for notification of progress events relating to the 1328 * chart. 1329 * 1330 * @param listener the object being registered. 1331 */ 1332 public void addProgressListener(ChartProgressListener listener) { 1333 this.progressListeners.add(ChartProgressListener.class, listener); 1334 } 1335 1336 /** 1337 * Deregisters an object for notification of changes to the chart. 1338 * 1339 * @param listener the object being deregistered. 1340 */ 1341 public void removeProgressListener(ChartProgressListener listener) { 1342 this.progressListeners.remove(ChartProgressListener.class, listener); 1343 } 1344 1345 /** 1346 * Sends a {@link ChartProgressEvent} to all registered listeners. 1347 * 1348 * @param event information about the event that triggered the 1349 * notification. 1350 */ 1351 protected void notifyListeners(ChartProgressEvent event) { 1352 1353 Object[] listeners = this.progressListeners.getListenerList(); 1354 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1355 if (listeners[i] == ChartProgressListener.class) { 1356 ((ChartProgressListener) listeners[i + 1]).chartProgress(event); 1357 } 1358 } 1359 1360 } 1361 1362 /** 1363 * Receives notification that a chart title has changed, and passes this 1364 * on to registered listeners. 1365 * 1366 * @param event information about the chart title change. 1367 */ 1368 public void titleChanged(TitleChangeEvent event) { 1369 event.setChart(this); 1370 notifyListeners(event); 1371 } 1372 1373 /** 1374 * Receives notification that the plot has changed, and passes this on to 1375 * registered listeners. 1376 * 1377 * @param event information about the plot change. 1378 */ 1379 public void plotChanged(PlotChangeEvent event) { 1380 event.setChart(this); 1381 notifyListeners(event); 1382 } 1383 1384 /** 1385 * Tests this chart for equality with another object. 1386 * 1387 * @param obj the object (<code>null</code> permitted). 1388 * 1389 * @return A boolean. 1390 */ 1391 public boolean equals(Object obj) { 1392 if (obj == this) { 1393 return true; 1394 } 1395 if (!(obj instanceof JFreeChart)) { 1396 return false; 1397 } 1398 JFreeChart that = (JFreeChart) obj; 1399 if (!this.renderingHints.equals(that.renderingHints)) { 1400 return false; 1401 } 1402 if (this.borderVisible != that.borderVisible) { 1403 return false; 1404 } 1405 if (!ObjectUtilities.equal(this.borderStroke, that.borderStroke)) { 1406 return false; 1407 } 1408 if (!PaintUtilities.equal(this.borderPaint, that.borderPaint)) { 1409 return false; 1410 } 1411 if (!this.padding.equals(that.padding)) { 1412 return false; 1413 } 1414 if (!ObjectUtilities.equal(this.title, that.title)) { 1415 return false; 1416 } 1417 if (!ObjectUtilities.equal(this.subtitles, that.subtitles)) { 1418 return false; 1419 } 1420 if (!ObjectUtilities.equal(this.plot, that.plot)) { 1421 return false; 1422 } 1423 if (!PaintUtilities.equal( 1424 this.backgroundPaint, that.backgroundPaint 1425 )) { 1426 return false; 1427 } 1428 if (!ObjectUtilities.equal(this.backgroundImage, 1429 that.backgroundImage)) { 1430 return false; 1431 } 1432 if (this.backgroundImageAlignment != that.backgroundImageAlignment) { 1433 return false; 1434 } 1435 if (this.backgroundImageAlpha != that.backgroundImageAlpha) { 1436 return false; 1437 } 1438 if (this.notify != that.notify) { 1439 return false; 1440 } 1441 return true; 1442 } 1443 1444 /** 1445 * Provides serialization support. 1446 * 1447 * @param stream the output stream. 1448 * 1449 * @throws IOException if there is an I/O error. 1450 */ 1451 private void writeObject(ObjectOutputStream stream) throws IOException { 1452 stream.defaultWriteObject(); 1453 SerialUtilities.writeStroke(this.borderStroke, stream); 1454 SerialUtilities.writePaint(this.borderPaint, stream); 1455 SerialUtilities.writePaint(this.backgroundPaint, stream); 1456 } 1457 1458 /** 1459 * Provides serialization support. 1460 * 1461 * @param stream the input stream. 1462 * 1463 * @throws IOException if there is an I/O error. 1464 * @throws ClassNotFoundException if there is a classpath problem. 1465 */ 1466 private void readObject(ObjectInputStream stream) 1467 throws IOException, ClassNotFoundException { 1468 stream.defaultReadObject(); 1469 this.borderStroke = SerialUtilities.readStroke(stream); 1470 this.borderPaint = SerialUtilities.readPaint(stream); 1471 this.backgroundPaint = SerialUtilities.readPaint(stream); 1472 this.progressListeners = new EventListenerList(); 1473 this.changeListeners = new EventListenerList(); 1474 this.renderingHints = new RenderingHints( 1475 RenderingHints.KEY_ANTIALIASING, 1476 RenderingHints.VALUE_ANTIALIAS_ON); 1477 1478 // register as a listener with sub-components... 1479 if (this.title != null) { 1480 this.title.addChangeListener(this); 1481 } 1482 1483 for (int i = 0; i < getSubtitleCount(); i++) { 1484 getSubtitle(i).addChangeListener(this); 1485 } 1486 this.plot.addChangeListener(this); 1487 } 1488 1489 /** 1490 * Prints information about JFreeChart to standard output. 1491 * 1492 * @param args no arguments are honored. 1493 */ 1494 public static void main(String[] args) { 1495 System.out.println(JFreeChart.INFO.toString()); 1496 } 1497 1498 /** 1499 * Clones the object, and takes care of listeners. 1500 * Note: caller shall register its own listeners on cloned graph. 1501 * 1502 * @return A clone. 1503 * 1504 * @throws CloneNotSupportedException if the chart is not cloneable. 1505 */ 1506 public Object clone() throws CloneNotSupportedException { 1507 JFreeChart chart = (JFreeChart) super.clone(); 1508 1509 chart.renderingHints = (RenderingHints) this.renderingHints.clone(); 1510 // private boolean borderVisible; 1511 // private transient Stroke borderStroke; 1512 // private transient Paint borderPaint; 1513 1514 if (this.title != null) { 1515 chart.title = (TextTitle) this.title.clone(); 1516 chart.title.addChangeListener(chart); 1517 } 1518 1519 chart.subtitles = new ArrayList(); 1520 for (int i = 0; i < getSubtitleCount(); i++) { 1521 Title subtitle = (Title) getSubtitle(i).clone(); 1522 chart.subtitles.add(subtitle); 1523 subtitle.addChangeListener(chart); 1524 } 1525 1526 if (this.plot != null) { 1527 chart.plot = (Plot) this.plot.clone(); 1528 chart.plot.addChangeListener(chart); 1529 } 1530 1531 chart.progressListeners = new EventListenerList(); 1532 chart.changeListeners = new EventListenerList(); 1533 return chart; 1534 } 1535 1536 } 1537 1538 /** 1539 * Information about the JFreeChart project. One instance of this class is 1540 * assigned to <code>JFreeChart.INFO<code>. 1541 */ 1542 class JFreeChartInfo extends ProjectInfo { 1543 1544 /** 1545 * Default constructor. 1546 */ 1547 public JFreeChartInfo() { 1548 1549 // get a locale-specific resource bundle... 1550 String baseResourceClass 1551 = "org.jfree.chart.resources.JFreeChartResources"; 1552 ResourceBundle resources = ResourceBundle.getBundle(baseResourceClass); 1553 1554 setName(resources.getString("project.name")); 1555 setVersion(resources.getString("project.version")); 1556 setInfo(resources.getString("project.info")); 1557 setCopyright(resources.getString("project.copyright")); 1558 setLogo(null); // load only when required 1559 setLicenceName("LGPL"); 1560 setLicenceText(Licences.getInstance().getLGPL()); 1561 1562 setContributors(Arrays.asList( 1563 new Contributor[]{ 1564 new Contributor("Eric Alexander", "-"), 1565 new Contributor( 1566 "Richard Atkinson", "richard_c_atkinson@ntlworld.com" 1567 ), 1568 new Contributor("David Basten", "-"), 1569 new Contributor("David Berry", "-"), 1570 new Contributor("Anthony Boulestreau", "-"), 1571 new Contributor("Jeremy Bowman", "-"), 1572 new Contributor("Nicolas Brodu", "-"), 1573 new Contributor("David Browning", "-"), 1574 new Contributor("Soren Caspersen", "-"), 1575 new Contributor("Chuanhao Chiu", "-"), 1576 new Contributor("Brian Cole", "-"), 1577 new Contributor("Pascal Collet", "-"), 1578 new Contributor("Martin Cordova", "-"), 1579 new Contributor("Paolo Cova", "-"), 1580 new Contributor("Mike Duffy", "-"), 1581 new Contributor("Don Elliott", "-"), 1582 new Contributor("Jonathan Gabbai", "-"), 1583 new Contributor("David Gilbert", 1584 "david.gilbert@object-refinery.com"), 1585 new Contributor("Serge V. Grachov", "-"), 1586 new Contributor("Daniel Gredler", "-"), 1587 new Contributor("Hans-Jurgen Greiner", "-"), 1588 new Contributor("Joao Guilherme Del Valle", "-"), 1589 new Contributor("Aiman Han", "-"), 1590 new Contributor("Cameron Hayne", "-"), 1591 new Contributor("Jon Iles", "-"), 1592 new Contributor("Wolfgang Irler", "-"), 1593 new Contributor("Adriaan Joubert", "-"), 1594 new Contributor("Darren Jung", "-"), 1595 new Contributor("Xun Kang", "-"), 1596 new Contributor("Bill Kelemen", "-"), 1597 new Contributor("Norbert Kiesel", "-"), 1598 new Contributor("Gideon Krause", "-"), 1599 new Contributor("Pierre-Marie Le Biot", "-"), 1600 new Contributor("Arnaud Lelievre", "-"), 1601 new Contributor("Wolfgang Lenhard", "-"), 1602 new Contributor("David Li", "-"), 1603 new Contributor("Yan Liu", "-"), 1604 new Contributor("Tin Luu", "-"), 1605 new Contributor("Craig MacFarlane", "-"), 1606 new Contributor("Achilleus Mantzios", "-"), 1607 new Contributor("Thomas Meier", "-"), 1608 new Contributor("Jim Moore", "-"), 1609 new Contributor("Jonathan Nash", "-"), 1610 new Contributor("Barak Naveh", "-"), 1611 new Contributor("David M. O'Donnell", "-"), 1612 new Contributor("Krzysztof Paz", "-"), 1613 new Contributor("Tomer Peretz", "-"), 1614 new Contributor("Andrzej Porebski", "-"), 1615 new Contributor("Xavier Poinsard", "-"), 1616 new Contributor("Viktor Rajewski", "-"), 1617 new Contributor("Eduardo Ramalho", "-"), 1618 new Contributor("Michael Rauch", "-"), 1619 new Contributor("Cameron Riley", "-"), 1620 new Contributor("Klaus Rheinwald", "-"), 1621 new Contributor("Dan Rivett", "d.rivett@ukonline.co.uk"), 1622 new Contributor("Scott Sams", "-"), 1623 new Contributor("Michel Santos", "-"), 1624 new Contributor("Thierry Saura", "-"), 1625 new Contributor("Andreas Schneider", "-"), 1626 new Contributor("Jean-Luc SCHWAB", "-"), 1627 new Contributor("Bryan Scott", "-"), 1628 new Contributor("Tobias Selb", "-"), 1629 new Contributor("Mofeed Shahin", "-"), 1630 new Contributor("Pady Srinivasan", "-"), 1631 new Contributor("Greg Steckman", "-"), 1632 new Contributor("Roger Studner", "-"), 1633 new Contributor("Irv Thomae", "-"), 1634 new Contributor("Eric Thomas", "-"), 1635 new Contributor("Rich Unger", "-"), 1636 new Contributor("Daniel van Enckevort", "-"), 1637 new Contributor("Laurence Vanhelsuwe", "-"), 1638 new Contributor("Sylvain Vieujot", "-"), 1639 new Contributor("Mark Watson", "www.markwatson.com"), 1640 new Contributor("Alex Weber", "-"), 1641 new Contributor("Matthew Wright", "-"), 1642 new Contributor("Benoit Xhenseval", "-"), 1643 new Contributor("Christian W. Zuckschwerdt", 1644 "Christian.Zuckschwerdt@Informatik.Uni-Oldenburg.de"), 1645 new Contributor("Hari", "-"), 1646 new Contributor("Sam (oldman)", "-"), 1647 } 1648 )); 1649 1650 addLibrary(JCommon.INFO); 1651 1652 } 1653 1654 /** 1655 * Returns the JFreeChart logo (a picture of a gorilla). 1656 * 1657 * @return The JFreeChart logo. 1658 */ 1659 public Image getLogo() { 1660 1661 Image logo = super.getLogo(); 1662 if (logo == null) { 1663 URL imageURL = this.getClass().getClassLoader().getResource( 1664 "org/jfree/chart/gorilla.jpg"); 1665 if (imageURL != null) { 1666 ImageIcon temp = new ImageIcon(imageURL); 1667 // use ImageIcon because it waits for the image to load... 1668 logo = temp.getImage(); 1669 setLogo(logo); 1670 } 1671 } 1672 return logo; 1673 1674 } 1675 1676 }