Source for org.jfree.chart.JFreeChart

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * ---------------
  28:  * JFreeChart.java
  29:  * ---------------
  30:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Andrzej Porebski;
  34:  *                   David Li;
  35:  *                   Wolfgang Irler;
  36:  *                   Christian W. Zuckschwerdt;
  37:  *                   Klaus Rheinwald;
  38:  *                   Nicolas Brodu;
  39:  *                   
  40:  * NOTE: The above list of contributors lists only the people that have
  41:  * contributed to this source file (JFreeChart.java) - for a list of ALL
  42:  * contributors to the project, please see the README.txt file.
  43:  *
  44:  * $Id: JFreeChart.java,v 1.34.2.17 2007/03/22 10:53:54 mungady Exp $
  45:  *
  46:  * Changes (from 20-Jun-2001)
  47:  * --------------------------
  48:  * 20-Jun-2001 : Modifications submitted by Andrzej Porebski for legend 
  49:  *               placement;
  50:  * 21-Jun-2001 : Removed JFreeChart parameter from Plot constructors (DG);
  51:  * 22-Jun-2001 : Multiple titles added (original code by David Berry, with 
  52:  *               reworkings by DG);
  53:  * 18-Sep-2001 : Updated header (DG);
  54:  * 15-Oct-2001 : Moved data source classes into new package 
  55:  *               com.jrefinery.data.* (DG);
  56:  * 18-Oct-2001 : New factory method for creating VerticalXYBarChart (DG);
  57:  * 19-Oct-2001 : Moved series paint and stroke methods to the Plot class (DG);
  58:  *               Moved static chart creation methods to new ChartFactory 
  59:  *               class (DG);
  60:  * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
  61:  *               Fixed bug where chart isn't registered with the dataset (DG);
  62:  * 07-Nov-2001 : Fixed bug where null title in constructor causes 
  63:  *               exception (DG);
  64:  *               Tidied up event notification code (DG);
  65:  * 17-Nov-2001 : Added getLegendItemCount() method (DG);
  66:  * 21-Nov-2001 : Set clipping in draw method to ensure that nothing gets drawn 
  67:  *               outside the chart area (DG);
  68:  * 11-Dec-2001 : Added the createBufferedImage() method, taken from the 
  69:  *               JFreeChartServletDemo class (DG);
  70:  * 13-Dec-2001 : Added tooltips (DG);
  71:  * 16-Jan-2002 : Added handleClick() method (DG);
  72:  * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG);
  73:  * 05-Feb-2002 : Removed redundant tooltips code (DG);
  74:  * 19-Feb-2002 : Added accessor methods for the backgroundImage and 
  75:  *               backgroundImageAlpha attributes (DG);
  76:  * 21-Feb-2002 : Added static fields for INFO, COPYRIGHT, LICENCE, CONTRIBUTORS
  77:  *               and LIBRARIES.  These can be used to display information about
  78:  *               JFreeChart (DG);
  79:  * 06-Mar-2002 : Moved constants to JFreeChartConstants interface (DG);
  80:  * 18-Apr-2002 : PieDataset is no longer sorted (oldman);
  81:  * 23-Apr-2002 : Moved dataset to the Plot class (DG);
  82:  * 13-Jun-2002 : Added an extra draw() method (DG);
  83:  * 25-Jun-2002 : Implemented the Drawable interface and removed redundant 
  84:  *               imports (DG);
  85:  * 26-Jun-2002 : Added another createBufferedImage() method (DG);
  86:  * 18-Sep-2002 : Fixed issues reported by Checkstyle (DG);
  87:  * 23-Sep-2002 : Added new contributor (DG);
  88:  * 28-Oct-2002 : Created main title and subtitle list to replace existing title
  89:  *               list (DG);
  90:  * 08-Jan-2003 : Added contributor (DG);
  91:  * 17-Jan-2003 : Added new constructor (DG);
  92:  * 22-Jan-2003 : Added ChartColor class by Cameron Riley, and background image 
  93:  *               alignment code by Christian W. Zuckschwerdt (DG);
  94:  * 11-Feb-2003 : Added flag to allow suppression of chart change events, based 
  95:  *               on a suggestion by Klaus Rheinwald (DG);
  96:  * 04-Mar-2003 : Added small fix for suppressed chart change events (see bug id
  97:  *               690865) (DG);
  98:  * 10-Mar-2003 : Added Benoit Xhenseval to contributors (DG);
  99:  * 26-Mar-2003 : Implemented Serializable (DG);
 100:  * 15-Jul-2003 : Added an optional border for the chart (DG);
 101:  * 11-Sep-2003 : Took care of listeners while cloning (NB);
 102:  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
 103:  * 22-Sep-2003 : Added nullpointer checks.
 104:  * 25-Sep-2003 : Added nullpointer checks too (NB).
 105:  * 03-Dec-2003 : Legends are now registered by this class instead of using the 
 106:  *               old constructor way (TM);
 107:  * 03-Dec-2003 : Added anchorPoint to draw() method (DG);
 108:  * 08-Jan-2004 : Reworked title code, introducing line wrapping (DG);
 109:  * 09-Feb-2004 : Created additional createBufferedImage() method (DG);
 110:  * 05-Apr-2004 : Added new createBufferedImage() method (DG);
 111:  * 27-May-2004 : Moved constants from JFreeChartConstants.java back to this 
 112:  *               class (DG);
 113:  * 25-Nov-2004 : Updates for changes to Title class (DG);
 114:  * 06-Jan-2005 : Change lookup for default background color (DG);
 115:  * 31-Jan-2005 : Added Don Elliott to contributors (DG);
 116:  * 02-Feb-2005 : Added clearSubtitles() method (DG);
 117:  * 03-Feb-2005 : Added Mofeed Shahin to contributors (DG);
 118:  * 08-Feb-2005 : Updated for RectangleConstraint changes (DG);
 119:  * 28-Mar-2005 : Renamed Legend --> OldLegend (DG);
 120:  * 12-Apr-2005 : Added methods to access legend(s) in subtitle list (DG);
 121:  * 13-Apr-2005 : Added removeLegend() and removeSubtitle() methods (DG);
 122:  * 20-Apr-2005 : Modified to collect chart entities from titles and 
 123:  *               subtitles (DG);
 124:  * 26-Apr-2005 : Removed LOGGER (DG);
 125:  * 06-Jun-2005 : Added addLegend() method and padding attribute, fixed equals() 
 126:  *               method (DG);
 127:  * 24-Nov-2005 : Removed OldLegend and related code - don't want to support
 128:  *               this in 1.0.0 final (DG);
 129:  * ------------- JFREECHART 1.0.x ---------------------------------------------
 130:  * 27-Jan-2006 : Updated version number (DG);
 131:  * 07-Dec-2006 : Added some missing credits (DG);
 132:  * 17-Jan-2007 : Added Darren Jung to contributor list (DG);
 133:  * 05-Mar-2007 : Added Sergei Ivanov to the contributor list (DG);
 134:  * 16-Mar-2007 : Modified initial legend border (DG);
 135:  * 22-Mar-2007 : New methods for text anti-aliasing (DG);
 136:  * 
 137:  */
 138: 
 139: package org.jfree.chart;
 140: 
 141: import java.awt.AlphaComposite;
 142: import java.awt.BasicStroke;
 143: import java.awt.Color;
 144: import java.awt.Composite;
 145: import java.awt.Font;
 146: import java.awt.Graphics2D;
 147: import java.awt.Image;
 148: import java.awt.Paint;
 149: import java.awt.RenderingHints;
 150: import java.awt.Shape;
 151: import java.awt.Stroke;
 152: import java.awt.geom.AffineTransform;
 153: import java.awt.geom.Point2D;
 154: import java.awt.geom.Rectangle2D;
 155: import java.awt.image.BufferedImage;
 156: import java.io.IOException;
 157: import java.io.ObjectInputStream;
 158: import java.io.ObjectOutputStream;
 159: import java.io.Serializable;
 160: import java.net.URL;
 161: import java.util.ArrayList;
 162: import java.util.Arrays;
 163: import java.util.Iterator;
 164: import java.util.List;
 165: import java.util.ResourceBundle;
 166: 
 167: import javax.swing.ImageIcon;
 168: import javax.swing.UIManager;
 169: import javax.swing.event.EventListenerList;
 170: 
 171: import org.jfree.JCommon;
 172: import org.jfree.chart.block.BlockParams;
 173: import org.jfree.chart.block.EntityBlockResult;
 174: import org.jfree.chart.block.LengthConstraintType;
 175: import org.jfree.chart.block.LineBorder;
 176: import org.jfree.chart.block.RectangleConstraint;
 177: import org.jfree.chart.entity.EntityCollection;
 178: import org.jfree.chart.event.ChartChangeEvent;
 179: import org.jfree.chart.event.ChartChangeListener;
 180: import org.jfree.chart.event.ChartProgressEvent;
 181: import org.jfree.chart.event.ChartProgressListener;
 182: import org.jfree.chart.event.PlotChangeEvent;
 183: import org.jfree.chart.event.PlotChangeListener;
 184: import org.jfree.chart.event.TitleChangeEvent;
 185: import org.jfree.chart.event.TitleChangeListener;
 186: import org.jfree.chart.plot.CategoryPlot;
 187: import org.jfree.chart.plot.Plot;
 188: import org.jfree.chart.plot.PlotRenderingInfo;
 189: import org.jfree.chart.plot.XYPlot;
 190: import org.jfree.chart.title.LegendTitle;
 191: import org.jfree.chart.title.TextTitle;
 192: import org.jfree.chart.title.Title;
 193: import org.jfree.data.Range;
 194: import org.jfree.io.SerialUtilities;
 195: import org.jfree.ui.Align;
 196: import org.jfree.ui.Drawable;
 197: import org.jfree.ui.HorizontalAlignment;
 198: import org.jfree.ui.RectangleEdge;
 199: import org.jfree.ui.RectangleInsets;
 200: import org.jfree.ui.Size2D;
 201: import org.jfree.ui.VerticalAlignment;
 202: import org.jfree.ui.about.Contributor;
 203: import org.jfree.ui.about.Licences;
 204: import org.jfree.ui.about.ProjectInfo;
 205: import org.jfree.util.ObjectUtilities;
 206: import org.jfree.util.PaintUtilities;
 207: 
 208: /**
 209:  * A chart class implemented using the Java 2D APIs.  The current version
 210:  * supports bar charts, line charts, pie charts and xy plots (including time
 211:  * series data).
 212:  * <P>
 213:  * JFreeChart coordinates several objects to achieve its aim of being able to
 214:  * draw a chart on a Java 2D graphics device: a list of {@link Title} objects
 215:  * (which often includes the chart's legend), a {@link Plot} and a 
 216:  * {@link org.jfree.data.general.Dataset} (the plot in turn manages a 
 217:  * domain axis and a range axis).
 218:  * <P>
 219:  * You should use a {@link ChartPanel} to display a chart in a GUI.
 220:  * <P>
 221:  * The {@link ChartFactory} class contains static methods for creating 
 222:  * 'ready-made' charts.
 223:  *
 224:  * @see ChartPanel
 225:  * @see ChartFactory
 226:  * @see Title
 227:  * @see Plot
 228:  */
 229: public class JFreeChart implements Drawable,
 230:                                    TitleChangeListener,
 231:                                    PlotChangeListener,
 232:                                    Serializable,
 233:                                    Cloneable {
 234: 
 235:     /** For serialization. */    
 236:     private static final long serialVersionUID = -3470703747817429120L;
 237:     
 238:     /** Information about the project. */
 239:     public static final ProjectInfo INFO = new JFreeChartInfo();
 240: 
 241:     /** The default font for titles. */
 242:     public static final Font DEFAULT_TITLE_FONT 
 243:             = new Font("SansSerif", Font.BOLD, 18);
 244: 
 245:     /** The default background color. */
 246:     public static final Paint DEFAULT_BACKGROUND_PAINT 
 247:             = UIManager.getColor("Panel.background");
 248: 
 249:     /** The default background image. */
 250:     public static final Image DEFAULT_BACKGROUND_IMAGE = null;
 251: 
 252:     /** The default background image alignment. */
 253:     public static final int DEFAULT_BACKGROUND_IMAGE_ALIGNMENT = Align.FIT;
 254: 
 255:     /** The default background image alpha. */
 256:     public static final float DEFAULT_BACKGROUND_IMAGE_ALPHA = 0.5f;
 257: 
 258:     /** 
 259:      * Rendering hints that will be used for chart drawing.  This should never
 260:      * be <code>null</code>. 
 261:      */
 262:     private transient RenderingHints renderingHints;
 263: 
 264:     /** A flag that controls whether or not the chart border is drawn. */
 265:     private boolean borderVisible;
 266: 
 267:     /** The stroke used to draw the chart border (if visible). */
 268:     private transient Stroke borderStroke;
 269: 
 270:     /** The paint used to draw the chart border (if visible). */
 271:     private transient Paint borderPaint;
 272: 
 273:     /** The padding between the chart border and the chart drawing area. */
 274:     private RectangleInsets padding;
 275:     
 276:     /** The chart title (optional). */
 277:     private TextTitle title;
 278: 
 279:     /** The chart subtitles (zero, one or many). */
 280:     private List subtitles;
 281: 
 282:     /** Draws the visual representation of the data. */
 283:     private Plot plot;
 284: 
 285:     /** Paint used to draw the background of the chart. */
 286:     private transient Paint backgroundPaint;
 287: 
 288:     /** An optional background image for the chart. */
 289:     private transient Image backgroundImage;  // todo: not serialized yet
 290: 
 291:     /** The alignment for the background image. */
 292:     private int backgroundImageAlignment = Align.FIT;
 293: 
 294:     /** The alpha transparency for the background image. */
 295:     private float backgroundImageAlpha = 0.5f;
 296: 
 297:     /** Storage for registered change listeners. */
 298:     private transient EventListenerList changeListeners;
 299: 
 300:     /** Storage for registered progress listeners. */
 301:     private transient EventListenerList progressListeners;
 302: 
 303:     /** 
 304:      * A flag that can be used to enable/disable notification of chart change 
 305:      * events. 
 306:      */
 307:     private boolean notify;
 308:     
 309:     /**
 310:      * Creates a new chart based on the supplied plot.  The chart will have
 311:      * a legend added automatically, but no title (although you can easily add
 312:      * one later).  
 313:      * <br><br>
 314:      * Note that the  {@link ChartFactory} class contains a range 
 315:      * of static methods that will return ready-made charts, and often this
 316:      * is a more convenient way to create charts than using this constructor.
 317:      *
 318:      * @param plot  the plot (<code>null</code> not permitted).
 319:      */
 320:     public JFreeChart(Plot plot) {
 321:         this(null, null, plot, true);
 322:     }
 323: 
 324:     /**
 325:      * Creates a new chart with the given title and plot.  A default font 
 326:      * (@link DEFAULT_TITLE_FONT) is used for the title, and the chart will 
 327:      * have a legend added automatically.  
 328:      * <br><br>
 329:      * Note that the  {@link ChartFactory} class contains a range 
 330:      * of static methods that will return ready-made charts, and often this
 331:      * is a more convenient way to create charts than using this constructor.
 332:      *
 333:      * @param title  the chart title (<code>null</code> permitted).
 334:      * @param plot  the plot (<code>null</code> not permitted).
 335:      */
 336:     public JFreeChart(String title, Plot plot) {
 337:         this(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true);
 338:     }
 339: 
 340:     /**
 341:      * Creates a new chart with the given title and plot.  The 
 342:      * <code>createLegend</code> argument specifies whether or not a legend
 343:      * should be added to the chart.  
 344:      * <br><br>
 345:      * Note that the  {@link ChartFactory} class contains a range 
 346:      * of static methods that will return ready-made charts, and often this
 347:      * is a more convenient way to create charts than using this constructor.
 348:      *
 349:      * @param title  the chart title (<code>null</code> permitted).
 350:      * @param titleFont  the font for displaying the chart title 
 351:      *                   (<code>null</code> permitted).
 352:      * @param plot  controller of the visual representation of the data 
 353:      *              (<code>null</code> not permitted).
 354:      * @param createLegend  a flag indicating whether or not a legend should   
 355:      *                      be created for the chart.
 356:      */
 357:     public JFreeChart(String title, Font titleFont, Plot plot, 
 358:                       boolean createLegend) {
 359: 
 360:         if (plot == null) {
 361:             throw new NullPointerException("Null 'plot' argument.");
 362:         }
 363: 
 364:         // create storage for listeners...
 365:         this.progressListeners = new EventListenerList();
 366:         this.changeListeners = new EventListenerList();
 367:         this.notify = true;  // default is to notify listeners when the 
 368:                              // chart changes
 369: 
 370:         this.renderingHints = new RenderingHints(
 371:                 RenderingHints.KEY_ANTIALIASING, 
 372:                 RenderingHints.VALUE_ANTIALIAS_ON);
 373: 
 374:         this.borderVisible = false;
 375:         this.borderStroke = new BasicStroke(1.0f);
 376:         this.borderPaint = Color.black;
 377: 
 378:         this.padding = RectangleInsets.ZERO_INSETS;
 379:         
 380:         this.plot = plot;
 381:         plot.addChangeListener(this);
 382: 
 383:         this.subtitles = new ArrayList();
 384: 
 385:         // create a legend, if requested...
 386:         if (createLegend) {
 387:             LegendTitle legend = new LegendTitle(this.plot);
 388:             legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0));
 389:             legend.setFrame(new LineBorder());
 390:             legend.setBackgroundPaint(Color.white);
 391:             legend.setPosition(RectangleEdge.BOTTOM);
 392:             this.subtitles.add(legend);
 393:         }
 394: 
 395:         // add the chart title, if one has been specified...
 396:         if (title != null) {
 397:             if (titleFont == null) {
 398:                 titleFont = DEFAULT_TITLE_FONT;
 399:             }
 400:             this.title = new TextTitle(title, titleFont);
 401:             this.title.addChangeListener(this);
 402:         }
 403: 
 404:         this.backgroundPaint = DEFAULT_BACKGROUND_PAINT;
 405: 
 406:         this.backgroundImage = DEFAULT_BACKGROUND_IMAGE;
 407:         this.backgroundImageAlignment = DEFAULT_BACKGROUND_IMAGE_ALIGNMENT;
 408:         this.backgroundImageAlpha = DEFAULT_BACKGROUND_IMAGE_ALPHA;
 409: 
 410:     }
 411: 
 412:     /**
 413:      * Returns the collection of rendering hints for the chart.
 414:      *
 415:      * @return The rendering hints for the chart (never <code>null</code>).
 416:      * 
 417:      * @see #setRenderingHints(RenderingHints)
 418:      */
 419:     public RenderingHints getRenderingHints() {
 420:         return this.renderingHints;
 421:     }
 422: 
 423:     /**
 424:      * Sets the rendering hints for the chart.  These will be added (using the 
 425:      * Graphics2D.addRenderingHints() method) near the start of the 
 426:      * JFreeChart.draw() method.
 427:      *
 428:      * @param renderingHints  the rendering hints (<code>null</code> not 
 429:      *                        permitted).
 430:      *                        
 431:      * @see #getRenderingHints()
 432:      */
 433:     public void setRenderingHints(RenderingHints renderingHints) {
 434:         if (renderingHints == null) {
 435:             throw new NullPointerException("RenderingHints given are null");
 436:         }
 437:         this.renderingHints = renderingHints;
 438:         fireChartChanged();
 439:     }
 440: 
 441:     /**
 442:      * Returns a flag that controls whether or not a border is drawn around the
 443:      * outside of the chart.
 444:      *
 445:      * @return A boolean.
 446:      * 
 447:      * @see #setBorderVisible(boolean)
 448:      */
 449:     public boolean isBorderVisible() {
 450:         return this.borderVisible;
 451:     }
 452: 
 453:     /**
 454:      * Sets a flag that controls whether or not a border is drawn around the 
 455:      * outside of the chart.
 456:      *
 457:      * @param visible  the flag.
 458:      * 
 459:      * @see #isBorderVisible()
 460:      */
 461:     public void setBorderVisible(boolean visible) {
 462:         this.borderVisible = visible;
 463:         fireChartChanged();
 464:     }
 465: 
 466:     /**
 467:      * Returns the stroke used to draw the chart border (if visible).
 468:      *
 469:      * @return The border stroke.
 470:      * 
 471:      * @see #setBorderStroke(Stroke)
 472:      */
 473:     public Stroke getBorderStroke() {
 474:         return this.borderStroke;
 475:     }
 476: 
 477:     /**
 478:      * Sets the stroke used to draw the chart border (if visible).
 479:      *
 480:      * @param stroke  the stroke.
 481:      * 
 482:      * @see #getBorderStroke()
 483:      */
 484:     public void setBorderStroke(Stroke stroke) {
 485:         this.borderStroke = stroke;
 486:         fireChartChanged();
 487:     }
 488: 
 489:     /**
 490:      * Returns the paint used to draw the chart border (if visible).
 491:      *
 492:      * @return The border paint.
 493:      * 
 494:      * @see #setBorderPaint(Paint)
 495:      */
 496:     public Paint getBorderPaint() {
 497:         return this.borderPaint;
 498:     }
 499: 
 500:     /**
 501:      * Sets the paint used to draw the chart border (if visible).
 502:      *
 503:      * @param paint  the paint.
 504:      * 
 505:      * @see #getBorderPaint()
 506:      */
 507:     public void setBorderPaint(Paint paint) {
 508:         this.borderPaint = paint;
 509:         fireChartChanged();
 510:     }
 511:     
 512:     /**
 513:      * Returns the padding between the chart border and the chart drawing area.
 514:      * 
 515:      * @return The padding (never <code>null</code>).
 516:      * 
 517:      * @see #setPadding(RectangleInsets)
 518:      */
 519:     public RectangleInsets getPadding() {
 520:         return this.padding;   
 521:     }
 522: 
 523:     /**
 524:      * Sets the padding between the chart border and the chart drawing area,
 525:      * and sends a {@link ChartChangeEvent} to all registered listeners.
 526:      * 
 527:      * @param padding  the padding (<code>null</code> not permitted).
 528:      * 
 529:      * @see #getPadding()
 530:      */
 531:     public void setPadding(RectangleInsets padding) {
 532:         if (padding == null) {
 533:             throw new IllegalArgumentException("Null 'padding' argument.");   
 534:         }
 535:         this.padding = padding;
 536:         notifyListeners(new ChartChangeEvent(this));
 537:     }
 538:     
 539:     /**
 540:      * Returns the main chart title.  Very often a chart will have just one
 541:      * title, so we make this case simple by providing accessor methods for
 542:      * the main title.  However, multiple titles are supported - see the
 543:      * {@link #addSubtitle(Title)} method.
 544:      *
 545:      * @return The chart title (possibly <code>null</code>).
 546:      * 
 547:      * @see #setTitle(TextTitle)
 548:      */
 549:     public TextTitle getTitle() {
 550:         return this.title;
 551:     }
 552: 
 553:     /**
 554:      * Sets the main title for the chart and sends a {@link ChartChangeEvent} 
 555:      * to all registered listeners.  If you do not want a title for the 
 556:      * chart, set it to <code>null</code>.  If you want more than one title on
 557:      * a chart, use the {@link #addSubtitle(Title)} method.
 558:      *
 559:      * @param title  the title (<code>null</code> permitted).
 560:      * 
 561:      * @see #getTitle()
 562:      */
 563:     public void setTitle(TextTitle title) {
 564:         this.title = title;
 565:         fireChartChanged();
 566:     }
 567: 
 568:     /**
 569:      * Sets the chart title and sends a {@link ChartChangeEvent} to all 
 570:      * registered listeners.  This is a convenience method that ends up calling 
 571:      * the {@link #setTitle(TextTitle)} method.  If there is an existing title,
 572:      * its text is updated, otherwise a new title using the default font is 
 573:      * added to the chart.  If <code>text</code> is <code>null</code> the chart
 574:      * title is set to <code>null</code>.
 575:      *
 576:      * @param text  the title text (<code>null</code> permitted).
 577:      * 
 578:      * @see #getTitle()
 579:      */
 580:     public void setTitle(String text) {
 581:         if (text != null) {
 582:             if (this.title == null) {
 583:                 setTitle(new TextTitle(text, JFreeChart.DEFAULT_TITLE_FONT));
 584:             }
 585:             else {
 586:                 this.title.setText(text);
 587:             }
 588:         }
 589:         else {
 590:             setTitle((TextTitle) null);
 591:         }
 592:     }
 593: 
 594:     /**
 595:      * Adds a legend to the plot and sends a {@link ChartChangeEvent} to all
 596:      * registered listeners.
 597:      * 
 598:      * @param legend  the legend (<code>null</code> not permitted).
 599:      * 
 600:      * @see #removeLegend()
 601:      */
 602:     public void addLegend(LegendTitle legend) {
 603:         addSubtitle(legend);    
 604:     }
 605:     
 606:     /**
 607:      * Returns the legend for the chart, if there is one.  Note that a chart
 608:      * can have more than one legend - this method returns the first.
 609:      * 
 610:      * @return The legend (possibly <code>null</code>).
 611:      * 
 612:      * @see #getLegend(int)
 613:      */
 614:     public LegendTitle getLegend() {
 615:         return getLegend(0);
 616:     }
 617:     
 618:     /**
 619:      * Returns the nth legend for a chart, or <code>null</code>.
 620:      * 
 621:      * @param index  the legend index (zero-based).
 622:      * 
 623:      * @return The legend (possibly <code>null</code>).
 624:      * 
 625:      * @see #addLegend(LegendTitle)
 626:      */
 627:     public LegendTitle getLegend(int index) {
 628:         int seen = 0;
 629:         Iterator iterator = this.subtitles.iterator();
 630:         while (iterator.hasNext()) {
 631:             Title subtitle = (Title) iterator.next();
 632:             if (subtitle instanceof LegendTitle) {
 633:                 if (seen == index) {
 634:                     return (LegendTitle) subtitle;
 635:                 }
 636:                 else {
 637:                     seen++;   
 638:                 }
 639:             }
 640:         }
 641:         return null;        
 642:     }
 643:     
 644:     /**
 645:      * Removes the first legend in the chart and sends a 
 646:      * {@link ChartChangeEvent} to all registered listeners.
 647:      * 
 648:      * @see #getLegend()
 649:      */
 650:     public void removeLegend() {
 651:         removeSubtitle(getLegend());
 652:     }
 653:     
 654:     /**
 655:      * Returns the list of subtitles for the chart.
 656:      *
 657:      * @return The subtitle list (possibly empty, but never <code>null</code>).
 658:      * 
 659:      * @see #setSubtitles(List)
 660:      */
 661:     public List getSubtitles() {
 662:         return this.subtitles;
 663:     }
 664: 
 665:     /**
 666:      * Sets the title list for the chart (completely replaces any existing 
 667:      * titles).
 668:      *
 669:      * @param subtitles  the new list of subtitles (<code>null</code> not 
 670:      *                   permitted).
 671:      *                   
 672:      * @see #getSubtitles()
 673:      */
 674:     public void setSubtitles(List subtitles) {
 675:         if (subtitles == null) {
 676:             throw new NullPointerException("Null 'subtitles' argument.");
 677:         }
 678:         this.subtitles = subtitles;
 679:         fireChartChanged();
 680:     }
 681: 
 682:     /**
 683:      * Returns the number of titles for the chart.
 684:      *
 685:      * @return The number of titles for the chart.
 686:      * 
 687:      * @see #getSubtitles()
 688:      */
 689:     public int getSubtitleCount() {
 690:         return this.subtitles.size();
 691:     }
 692: 
 693:     /**
 694:      * Returns a chart subtitle.
 695:      *
 696:      * @param index  the index of the chart subtitle (zero based).
 697:      *
 698:      * @return A chart subtitle.
 699:      * 
 700:      * @see #addSubtitle(Title)
 701:      */
 702:     public Title getSubtitle(int index) {
 703:         if ((index < 0) || (index == getSubtitleCount())) {
 704:             throw new IllegalArgumentException("Index out of range.");
 705:         }
 706:         return (Title) this.subtitles.get(index);
 707:     }
 708: 
 709:     /**
 710:      * Adds a chart subtitle, and notifies registered listeners that the chart 
 711:      * has been modified.
 712:      *
 713:      * @param subtitle  the subtitle (<code>null</code> not permitted).
 714:      * 
 715:      * @see #getSubtitle(int)
 716:      */
 717:     public void addSubtitle(Title subtitle) {
 718:         if (subtitle == null) {
 719:             throw new IllegalArgumentException("Null 'subtitle' argument.");
 720:         }
 721:         this.subtitles.add(subtitle);
 722:         subtitle.addChangeListener(this);
 723:         fireChartChanged();
 724:     }
 725:     
 726:     /**
 727:      * Clears all subtitles from the chart and sends a {@link ChartChangeEvent}
 728:      * to all registered listeners.
 729:      * 
 730:      * @see #addSubtitle(Title)
 731:      */
 732:     public void clearSubtitles() {
 733:         Iterator iterator = this.subtitles.iterator();
 734:         while (iterator.hasNext()) {
 735:             Title t = (Title) iterator.next();
 736:             t.removeChangeListener(this);
 737:         }
 738:         this.subtitles.clear();
 739:         fireChartChanged();
 740:     }
 741: 
 742:     /**
 743:      * Removes the specified subtitle and sends a {@link ChartChangeEvent} to
 744:      * all registered listeners.
 745:      * 
 746:      * @param title  the title.
 747:      * 
 748:      * @see #addSubtitle(Title)
 749:      */
 750:     public void removeSubtitle(Title title) {
 751:         this.subtitles.remove(title);
 752:         fireChartChanged();
 753:     }
 754:     
 755:     /**
 756:      * Returns the plot for the chart.  The plot is a class responsible for
 757:      * coordinating the visual representation of the data, including the axes
 758:      * (if any).
 759:      *
 760:      * @return The plot.
 761:      */
 762:     public Plot getPlot() {
 763:         return this.plot;
 764:     }
 765: 
 766:     /**
 767:      * Returns the plot cast as a {@link CategoryPlot}.
 768:      * <p>
 769:      * NOTE: if the plot is not an instance of {@link CategoryPlot}, then a
 770:      * <code>ClassCastException</code> is thrown.
 771:      *
 772:      * @return The plot.
 773:      * 
 774:      * @see #getPlot()
 775:      */
 776:     public CategoryPlot getCategoryPlot() {
 777:         return (CategoryPlot) this.plot;
 778:     }
 779: 
 780:     /**
 781:      * Returns the plot cast as an {@link XYPlot}.
 782:      * <p>
 783:      * NOTE: if the plot is not an instance of {@link XYPlot}, then a
 784:      * <code>ClassCastException</code> is thrown.
 785:      *
 786:      * @return The plot.
 787:      * 
 788:      * @see #getPlot()
 789:      */
 790:     public XYPlot getXYPlot() {
 791:         return (XYPlot) this.plot;
 792:     }
 793: 
 794:     /**
 795:      * Returns a flag that indicates whether or not anti-aliasing is used when
 796:      * the chart is drawn.
 797:      *
 798:      * @return The flag.
 799:      * 
 800:      * @see #setAntiAlias(boolean)
 801:      */
 802:     public boolean getAntiAlias() {
 803:         Object val = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING);
 804:         return RenderingHints.VALUE_ANTIALIAS_ON.equals(val);
 805:     }
 806:     
 807:     /**
 808:      * Sets a flag that indicates whether or not anti-aliasing is used when the
 809:      * chart is drawn.
 810:      * <P>
 811:      * Anti-aliasing usually improves the appearance of charts, but is slower.
 812:      *
 813:      * @param flag  the new value of the flag.
 814:      * 
 815:      * @see #getAntiAlias()
 816:      */
 817:     public void setAntiAlias(boolean flag) {
 818: 
 819:         Object val = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING);
 820:         if (val == null) {
 821:             val = RenderingHints.VALUE_ANTIALIAS_DEFAULT;
 822:         }
 823:         if (!flag && RenderingHints.VALUE_ANTIALIAS_OFF.equals(val) 
 824:             || flag && RenderingHints.VALUE_ANTIALIAS_ON.equals(val)) {
 825:             // no change, do nothing
 826:             return;
 827:         }
 828:         if (flag) {
 829:             this.renderingHints.put(RenderingHints.KEY_ANTIALIASING, 
 830:                                     RenderingHints.VALUE_ANTIALIAS_ON);
 831:         }
 832:         else {
 833:             this.renderingHints.put(RenderingHints.KEY_ANTIALIASING, 
 834:                                     RenderingHints.VALUE_ANTIALIAS_OFF);
 835:         }
 836:         fireChartChanged();
 837: 
 838:     }
 839: 
 840:     /**
 841:      * Returns the current value stored in the rendering hints table for
 842:      * {@link RenderingHints#KEY_TEXT_ANTIALIASING}.
 843:      * 
 844:      * @return The hint value (possibly <code>null</code>).
 845:      * 
 846:      * @since 1.0.5
 847:      * 
 848:      * @see #setTextAntiAlias(Object)
 849:      */
 850:     public Object getTextAntiAlias() {
 851:         return this.renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); 
 852:     }
 853:     
 854:     /**
 855:      * Sets the value in the rendering hints table for 
 856:      * {@link RenderingHints#KEY_TEXT_ANTIALIASING} to either
 857:      * {@link RenderingHints#VALUE_TEXT_ANTIALIAS_ON} or
 858:      * {@link RenderingHints#VALUE_TEXT_ANTIALIAS_OFF}, then sends a 
 859:      * {@link ChartChangeEvent} to all registered listeners.
 860:      * 
 861:      * @param flag
 862:      * 
 863:      * @since 1.0.5
 864:      * 
 865:      * @see #getTextAntiAlias()
 866:      * @see #setTextAntiAlias(Object)
 867:      */
 868:     public void setTextAntiAlias(boolean flag) {
 869:         if (flag) {
 870:             setTextAntiAlias(RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 871:         }
 872:         else {
 873:             setTextAntiAlias(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);            
 874:         }
 875:     }
 876: 
 877:     /**
 878:      * Sets the value in the rendering hints table for 
 879:      * {@link RenderingHints#KEY_TEXT_ANTIALIASING} and sends a 
 880:      * {@link ChartChangeEvent} to all registered listeners.
 881:      * 
 882:      * @param val  the new value (<code>null</code> permitted).
 883:      * 
 884:      * @since 1.0.5
 885:      * 
 886:      * @see #getTextAntiAlias()
 887:      * @see #setTextAntiAlias(boolean)
 888:      */
 889:     public void setTextAntiAlias(Object val) {
 890:         this.renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, val);
 891:         this.notifyListeners(new ChartChangeEvent(this));
 892:     }
 893:     
 894:     /**
 895:      * Returns the paint used for the chart background.
 896:      *
 897:      * @return The paint (possibly <code>null</code>).
 898:      * 
 899:      * @see #setBackgroundPaint(Paint)
 900:      */
 901:     public Paint getBackgroundPaint() {
 902:         return this.backgroundPaint;
 903:     }
 904: 
 905:     /**
 906:      * Sets the paint used to fill the chart background and sends a 
 907:      * {@link ChartChangeEvent} to all registered listeners.
 908:      *
 909:      * @param paint  the paint (<code>null</code> permitted).
 910:      * 
 911:      * @see #getBackgroundPaint()
 912:      */
 913:     public void setBackgroundPaint(Paint paint) {
 914: 
 915:         if (this.backgroundPaint != null) {
 916:             if (!this.backgroundPaint.equals(paint)) {
 917:                 this.backgroundPaint = paint;
 918:                 fireChartChanged();
 919:             }
 920:         }
 921:         else {
 922:             if (paint != null) {
 923:                 this.backgroundPaint = paint;
 924:                 fireChartChanged();
 925:             }
 926:         }
 927: 
 928:     }
 929: 
 930:     /**
 931:      * Returns the background image for the chart, or <code>null</code> if 
 932:      * there is no image.
 933:      *
 934:      * @return The image (possibly <code>null</code>).
 935:      * 
 936:      * @see #setBackgroundImage(Image)
 937:      */
 938:     public Image getBackgroundImage() {
 939:         return this.backgroundImage;
 940:     }
 941: 
 942:     /**
 943:      * Sets the background image for the chart and sends a 
 944:      * {@link ChartChangeEvent} to all registered listeners.
 945:      *
 946:      * @para