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