JFreeChart

Overview Package Class Use Source Tree Index Deprecated About
Frames | No Frames
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:      * @param image  the image (<code>null</code> permitted).
 947:      * 
 948:      * @see #getBackgroundImage()
 949:      */
 950:     public void setBackgroundImage(Image image) {
 951: 
 952:         if (this.backgroundImage != null) {
 953:             if (!this.backgroundImage.equals(image)) {
 954:                 this.backgroundImage = image;
 955:                 fireChartChanged();
 956:             }
 957:         }
 958:         else {
 959:             if (image != null) {
 960:                 this.backgroundImage = image;
 961:                 fireChartChanged();
 962:             }
 963:         }
 964: 
 965:     }
 966: 
 967:     /**
 968:      * Returns the background image alignment. Alignment constants are defined 
 969:      * in the <code>org.jfree.ui.Align</code> class in the JCommon class 
 970:      * library.
 971:      *
 972:      * @return The alignment.
 973:      * 
 974:      * @see #setBackgroundImageAlignment(int)
 975:      */
 976:     public int getBackgroundImageAlignment() {
 977:         return this.backgroundImageAlignment;
 978:     }
 979: 
 980:     /**
 981:      * Sets the background alignment.  Alignment options are defined by the 
 982:      * {@link org.jfree.ui.Align} class.
 983:      *
 984:      * @param alignment  the alignment.
 985:      * 
 986:      * @see #getBackgroundImageAlignment()
 987:      */
 988:     public void setBackgroundImageAlignment(int alignment) {
 989:         if (this.backgroundImageAlignment != alignment) {
 990:             this.backgroundImageAlignment = alignment;
 991:             fireChartChanged();
 992:         }
 993:     }
 994: 
 995:     /**
 996:      * Returns the alpha-transparency for the chart's background image.
 997:      *
 998:      * @return The alpha-transparency.
 999:      * 
1000:      * @see #setBackgroundImageAlpha(float)
1001:      */
1002:     public float getBackgroundImageAlpha() {
1003:         return this.backgroundImageAlpha;
1004:     }
1005: 
1006:     /**
1007:      * Sets the alpha-transparency for the chart's background image.
1008:      * Registered listeners are notified that the chart has been changed.
1009:      *
1010:      * @param alpha  the alpha value.
1011:      * 
1012:      * @see #getBackgroundImageAlpha()
1013:      */
1014:     public void setBackgroundImageAlpha(float alpha) {
1015: 
1016:         if (this.backgroundImageAlpha != alpha) {
1017:             this.backgroundImageAlpha = alpha;
1018:             fireChartChanged();
1019:         }
1020: 
1021:     }
1022: 
1023:     /**
1024:      * Returns a flag that controls whether or not change events are sent to 
1025:      * registered listeners.
1026:      *
1027:      * @return A boolean.
1028:      * 
1029:      * @see #setNotify(boolean)
1030:      */
1031:     public boolean isNotify() {
1032:         return this.notify;
1033:     }
1034: 
1035:     /**
1036:      * Sets a flag that controls whether or not listeners receive 
1037:      * {@link ChartChangeEvent} notifications.
1038:      *
1039:      * @param notify  a boolean.
1040:      * 
1041:      * @see #isNotify()
1042:      */
1043:     public void setNotify(boolean notify) {
1044:         this.notify = notify;
1045:         // if the flag is being set to true, there may be queued up changes...
1046:         if (notify) {
1047:             notifyListeners(new ChartChangeEvent(this));
1048:         }
1049:     }
1050: 
1051:     /**
1052:      * Draws the chart on a Java 2D graphics device (such as the screen or a
1053:      * printer).
1054:      * <P>
1055:      * This method is the focus of the entire JFreeChart library.
1056:      *
1057:      * @param g2  the graphics device.
1058:      * @param area  the area within which the chart should be drawn.
1059:      */
1060:     public void draw(Graphics2D g2, Rectangle2D area) {
1061:         draw(g2, area, null, null);
1062:     }
1063: 
1064:     /**
1065:      * Draws the chart on a Java 2D graphics device (such as the screen or a
1066:      * printer).  This method is the focus of the entire JFreeChart library.
1067:      *
1068:      * @param g2  the graphics device.
1069:      * @param area  the area within which the chart should be drawn.
1070:      * @param info  records info about the drawing (null means collect no info).
1071:      */
1072:     public void draw(Graphics2D g2, Rectangle2D area, ChartRenderingInfo info) {
1073:         draw(g2, area, null, info);
1074:     }
1075:     
1076:     /**
1077:      * Draws the chart on a Java 2D graphics device (such as the screen or a
1078:      * printer).
1079:      * <P>
1080:      * This method is the focus of the entire JFreeChart library.
1081:      *
1082:      * @param g2  the graphics device.
1083:      * @param chartArea  the area within which the chart should be drawn.
1084:      * @param anchor  the anchor point (in Java2D space) for the chart 
1085:      *                (<code>null</code> permitted).
1086:      * @param info  records info about the drawing (null means collect no info).
1087:      */
1088:     public void draw(Graphics2D g2, 
1089:                      Rectangle2D chartArea, Point2D anchor, 
1090:                      ChartRenderingInfo info) {
1091: 
1092:         notifyListeners(new ChartProgressEvent(this, this, 
1093:                 ChartProgressEvent.DRAWING_STARTED, 0));
1094: 
1095:         // record the chart area, if info is requested...
1096:         if (info != null) {
1097:             info.clear();
1098:             info.setChartArea(chartArea);
1099:         }
1100: 
1101:         // ensure no drawing occurs outside chart area...
1102:         Shape savedClip = g2.getClip();
1103:         g2.clip(chartArea);
1104: 
1105:         g2.addRenderingHints(this.renderingHints);
1106: 
1107:         // draw the chart background...
1108:         if (this.backgroundPaint != null) {
1109:             g2.setPaint(this.backgroundPaint);
1110:             g2.fill(chartArea);
1111:         }
1112: 
1113:         if (this.backgroundImage != null) {
1114:             Composite originalComposite = g2.getComposite();
1115:             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 
1116:                     this.backgroundImageAlpha));
1117:             Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, 
1118:                     this.backgroundImage.getWidth(null), 
1119:                     this.backgroundImage.getHeight(null));
1120:             Align.align(dest, chartArea, this.backgroundImageAlignment);
1121:             g2.drawImage(this.backgroundImage, (int) dest.getX(), 
1122:                     (int) dest.getY(), (int) dest.getWidth(), 
1123:                     (int) dest.getHeight(), null);
1124:             g2.setComposite(originalComposite);
1125:         }
1126: 
1127:         if (isBorderVisible()) {
1128:             Paint paint = getBorderPaint();
1129:             Stroke stroke = getBorderStroke();
1130:             if (paint != null && stroke != null) {
1131:                 Rectangle2D borderArea = new Rectangle2D.Double(
1132:                         chartArea.getX(), chartArea.getY(), 
1133:                         chartArea.getWidth() - 1.0, chartArea.getHeight() 
1134:                         - 1.0);
1135:                 g2.setPaint(paint);
1136:                 g2.setStroke(stroke);
1137:                 g2.draw(borderArea);
1138:             }
1139:         }
1140: 
1141:         // draw the title and subtitles...
1142:         Rectangle2D nonTitleArea = new Rectangle2D.Double();
1143:         nonTitleArea.setRect(chartArea);
1144:         this.padding.trim(nonTitleArea);
1145:         
1146:         EntityCollection entities = null;
1147:         if (info != null) {
1148:             entities = info.getEntityCollection();   
1149:         }
1150:         if (this.title != null) {
1151:             EntityCollection e = drawTitle(this.title, g2, nonTitleArea, 
1152:                     (entities != null));
1153:             if (e != null) {
1154:                 entities.addAll(e);   
1155:             }
1156:         }
1157: 
1158:         Iterator iterator = this.subtitles.iterator();
1159:         while (iterator.hasNext()) {
1160:             Title currentTitle = (Title) iterator.next();
1161:             EntityCollection e = drawTitle(currentTitle, g2, nonTitleArea, 
1162:                     (entities != null));
1163:             if (e != null) {
1164:                 entities.addAll(e);   
1165:             }
1166:         }
1167: 
1168:         Rectangle2D plotArea = nonTitleArea;
1169:  
1170:         // draw the plot (axes and data visualisation)
1171:         PlotRenderingInfo plotInfo = null;
1172:         if (info != null) {
1173:             plotInfo = info.getPlotInfo();
1174:         }
1175:         this.plot.draw(g2, plotArea, anchor, null, plotInfo);
1176: 
1177:         g2.setClip(savedClip);
1178: 
1179:         notifyListeners(new ChartProgressEvent(this, this, 
1180:                 ChartProgressEvent.DRAWING_FINISHED, 100));
1181:     }
1182: 
1183:     /**
1184:      * Creates a rectangle that is aligned to the frame.
1185:      * 
1186:      * @param dimensions
1187:      * @param frame
1188:      * @param hAlign
1189:      * @param vAlign
1190:      * 
1191:      * @return A rectangle.
1192:      */
1193:     private Rectangle2D createAlignedRectangle2D(Size2D dimensions, 
1194:             Rectangle2D frame, HorizontalAlignment hAlign, 
1195:             VerticalAlignment vAlign) {
1196:         double x = Double.NaN;
1197:         double y = Double.NaN;
1198:         if (hAlign == HorizontalAlignment.LEFT) {
1199:             x = frame.getX();   
1200:         }
1201:         else if (hAlign == HorizontalAlignment.CENTER) {
1202:             x = frame.getCenterX() - (dimensions.width / 2.0);   
1203:         }
1204:         else if (hAlign == HorizontalAlignment.RIGHT) {
1205:             x = frame.getMaxX() - dimensions.width;   
1206:         }
1207:         if (vAlign == VerticalAlignment.TOP) {
1208:             y = frame.getY();   
1209:         }
1210:         else if (vAlign == VerticalAlignment.CENTER) {
1211:             y = frame.getCenterY() - (dimensions.height / 2.0);   
1212:         }
1213:         else if (vAlign == VerticalAlignment.BOTTOM) {
1214:             y = frame.getMaxY() - dimensions.height;   
1215:         }
1216:         
1217:         return new Rectangle2D.Double(x, y, dimensions.width, 
1218:                 dimensions.height);
1219:     }
1220:     
1221:     /**
1222:      * Draws a title.  The title should be drawn at the top, bottom, left or 
1223:      * right of the specified area, and the area should be updated to reflect 
1224:      * the amount of space used by the title.
1225:      *
1226:      * @param t  the title (<code>null</code> not permitted).
1227:      * @param g2  the graphics device (<code>null</code> not permitted).
1228:      * @param area  the chart area, excluding any existing titles 
1229:      *              (<code>null</code> not permitted).
1230:      * @param entities  a flag that controls whether or not an entity 
1231:      *                  collection is returned for the title.
1232:      * 
1233:      * @return An entity collection for the title (possibly <code>null</code>).
1234:      */
1235:     protected EntityCollection drawTitle(Title t, Graphics2D g2, 
1236:                                          Rectangle2D area, boolean entities) {
1237: 
1238:         if (t == null) {
1239:             throw new IllegalArgumentException("Null 't' argument.");   
1240:         }
1241:         if (area == null) {
1242:             throw new IllegalArgumentException("Null 'area' argument.");   
1243:         }
1244:         Rectangle2D titleArea = new Rectangle2D.Double();
1245:         RectangleEdge position = t.getPosition();
1246:         double ww = area.getWidth();
1247:         if (ww <= 0.0) {
1248:             return null;
1249:         }
1250:         double hh = area.getHeight();
1251:         if (hh <= 0.0) {
1252:             return null;
1253:         }
1254:         RectangleConstraint constraint = new RectangleConstraint(ww, 
1255:                 new Range(0.0, ww), LengthConstraintType.RANGE, hh, 
1256:                 new Range(0.0, hh), LengthConstraintType.RANGE);
1257:         Object retValue = null;
1258:         BlockParams p = new BlockParams();
1259:         p.setGenerateEntities(entities);
1260:         if (position == RectangleEdge.TOP) {
1261:             Size2D size = t.arrange(g2, constraint);
1262:             titleArea = createAlignedRectangle2D(size, area, 
1263:                     t.getHorizontalAlignment(), VerticalAlignment.TOP);
1264:             retValue = t.draw(g2, titleArea, p);
1265:             area.setRect(area.getX(), Math.min(area.getY() + size.height, 
1266:                     area.getMaxY()), area.getWidth(), Math.max(area.getHeight()
1267:                     - size.height, 0));
1268:         }
1269:         else if (position == RectangleEdge.BOTTOM) {
1270:             Size2D size = t.arrange(g2, constraint);
1271:             titleArea = createAlignedRectangle2D(size, area, 
1272:                     t.getHorizontalAlignment(), VerticalAlignment.BOTTOM);
1273:             retValue = t.draw(g2, titleArea, p);
1274:             area.setRect(area.getX(), area.getY(), area.getWidth(), 
1275:                     area.getHeight() - size.height);
1276:         }
1277:         else if (position == RectangleEdge.RIGHT) {
1278:             Size2D size = t.arrange(g2, constraint);
1279:             titleArea = createAlignedRectangle2D(size, area, 
1280:                     HorizontalAlignment.RIGHT, t.getVerticalAlignment());
1281:             retValue = t.draw(g2, titleArea, p);
1282:             area.setRect(area.getX(), area.getY(), area.getWidth() 
1283:                     - size.width, area.getHeight());
1284:         }
1285: 
1286:         else if (position == RectangleEdge.LEFT) {
1287:             Size2D size = t.arrange(g2, constraint);
1288:             titleArea = createAlignedRectangle2D(size, area, 
1289:                     HorizontalAlignment.LEFT, t.getVerticalAlignment());
1290:             retValue = t.draw(g2, titleArea, p);
1291:             area.setRect(area.getX() + size.width, area.getY(), area.getWidth() 
1292:                     - size.width, area.getHeight());
1293:         }
1294:         else {
1295:             throw new RuntimeException("Unrecognised title position.");
1296:         }
1297:         EntityCollection result = null;
1298:         if (retValue instanceof EntityBlockResult) {
1299:             EntityBlockResult ebr = (EntityBlockResult) retValue;
1300:             result = ebr.getEntityCollection();
1301:         }
1302:         return result;   
1303:     }
1304: 
1305:     /**
1306:      * Creates and returns a buffered image into which the chart has been drawn.
1307:      *
1308:      * @param width  the width.
1309:      * @param height  the height.
1310:      *
1311:      * @return A buffered image.
1312:      */
1313:     public BufferedImage createBufferedImage(int width, int height) {
1314:         return createBufferedImage(width, height, null);
1315:     }
1316: 
1317:     /**
1318:      * Creates and returns a buffered image into which the chart has been drawn.
1319:      *
1320:      * @param width  the width.
1321:      * @param height  the height.
1322:      * @param info  carries back chart state information (<code>null</code> 
1323:      *              permitted).
1324:      *
1325:      * @return A buffered image.
1326:      */
1327:     public BufferedImage createBufferedImage(int width, int height, 
1328:                                              ChartRenderingInfo info) {
1329:         return createBufferedImage(width, height, BufferedImage.TYPE_INT_RGB, 
1330:                 info);
1331:     }
1332: 
1333:     /**
1334:      * Creates and returns a buffered image into which the chart has been drawn.
1335:      *
1336:      * @param width  the width.
1337:      * @param height  the height.
1338:      * @param imageType  the image type.
1339:      * @param info  carries back chart state information (<code>null</code> 
1340:      *              permitted).
1341:      *
1342:      * @return A buffered image.
1343:      */
1344:     public BufferedImage createBufferedImage(int width, int height, 
1345:                                              int imageType, 
1346:                                              ChartRenderingInfo info) {
1347:         BufferedImage image = new BufferedImage(width, height, imageType);
1348:         Graphics2D g2 = image.createGraphics();
1349:         draw(g2, new Rectangle2D.Double(0, 0, width, height), null, info);
1350:         g2.dispose();
1351:         return image;
1352:     }
1353: 
1354:     /**
1355:      * Creates and returns a buffered image into which the chart has been drawn.
1356:      *
1357:      * @param imageWidth  the image width.
1358:      * @param imageHeight  the image height.
1359:      * @param drawWidth  the width for drawing the chart (will be scaled to 
1360:      *                   fit image).
1361:      * @param drawHeight  the height for drawing the chart (will be scaled to 
1362:      *                    fit image).
1363:      * @param info  optional object for collection chart dimension and entity 
1364:      *              information.
1365:      *
1366:      * @return A buffered image.
1367:      */
1368:     public BufferedImage createBufferedImage(int imageWidth, 
1369:                                              int imageHeight,
1370:                                              double drawWidth, 
1371:                                              double drawHeight, 
1372:                                              ChartRenderingInfo info) {
1373: 
1374:         BufferedImage image = new BufferedImage(imageWidth, imageHeight, 
1375:                 BufferedImage.TYPE_INT_RGB);
1376:         Graphics2D g2 = image.createGraphics();
1377:         double scaleX = imageWidth / drawWidth;
1378:         double scaleY = imageHeight / drawHeight;
1379:         AffineTransform st = AffineTransform.getScaleInstance(scaleX, scaleY);
1380:         g2.transform(st);
1381:         draw(g2, new Rectangle2D.Double(0, 0, drawWidth, drawHeight), null, 
1382:                 info);
1383:         g2.dispose();
1384:         return image;
1385: 
1386:     }
1387: 
1388:     /**
1389:      * Handles a 'click' on the chart.
1390:      * <P>
1391:      * JFreeChart is not a UI component, so some other object (e.g. ChartPanel)
1392:      * needs to capture the click event and pass it onto the JFreeChart object.
1393:      * If you are not using JFreeChart in a client application, then this
1394:      * method is not required (and hopefully it doesn't get in the way).
1395:      *
1396:      * @param x  x-coordinate of the click (in Java2D space).
1397:      * @param y  y-coordinate of the click (in Java2D space).
1398:      * @param info  contains chart dimension and entity information.
1399:      */
1400:     public void handleClick(int x, int y, ChartRenderingInfo info) {
1401: 
1402:         // pass the click on to the plot...
1403:         // rely on the plot to post a plot change event and redraw the chart...
1404:         this.plot.handleClick(x, y, info.getPlotInfo());
1405: 
1406:     }
1407: 
1408:     /**
1409:      * Registers an object for notification of changes to the chart.
1410:      *
1411:      * @param listener  the listener (<code>null</code> not permitted).
1412:      * 
1413:      * @see #removeChangeListener(ChartChangeListener)
1414:      */
1415:     public void addChangeListener(ChartChangeListener listener) {
1416:         if (listener == null) {
1417:             throw new IllegalArgumentException("Null 'listener' argument.");
1418:         }
1419:         this.changeListeners.add(ChartChangeListener.class, listener);
1420:     }
1421: 
1422:     /**
1423:      * Deregisters an object for notification of changes to the chart.
1424:      *
1425:      * @param listener  the listener (<code>null</code> not permitted)
1426:      * 
1427:      * @see #addChangeListener(ChartChangeListener)
1428:      */
1429:     public void removeChangeListener(ChartChangeListener listener) {
1430:         if (listener == null) {
1431:             throw new IllegalArgumentException("Null 'listener' argument.");
1432:         }
1433:         this.changeListeners.remove(ChartChangeListener.class, listener);
1434:     }
1435: 
1436:     /**
1437:      * Sends a default {@link ChartChangeEvent} to all registered listeners.
1438:      * <P>
1439:      * This method is for convenience only.
1440:      */
1441:     public void fireChartChanged() {
1442:         ChartChangeEvent event = new ChartChangeEvent(this);
1443:         notifyListeners(event);
1444:     }
1445: 
1446:     /**
1447:      * Sends a {@link ChartChangeEvent} to all registered listeners.
1448:      *
1449:      * @param event  information about the event that triggered the 
1450:      *               notification.
1451:      */
1452:     protected void notifyListeners(ChartChangeEvent event) {
1453:         if (this.notify) {
1454:             Object[] listeners = this.changeListeners.getListenerList();
1455:             for (int i = listeners.length - 2; i >= 0; i -= 2) {
1456:                 if (listeners[i] == ChartChangeListener.class) {
1457:                     ((ChartChangeListener) listeners[i + 1]).chartChanged(
1458:                             event);
1459:                 }
1460:             }
1461:         }
1462:     }
1463: 
1464:     /**
1465:      * Registers an object for notification of progress events relating to the 
1466:      * chart.
1467:      *
1468:      * @param listener  the object being registered.
1469:      * 
1470:      * @see #removeProgressListener(ChartProgressListener)
1471:      */
1472:     public void addProgressListener(ChartProgressListener listener) {
1473:         this.progressListeners.add(ChartProgressListener.class, listener);
1474:     }
1475: 
1476:     /**
1477:      * Deregisters an object for notification of changes to the chart.
1478:      *
1479:      * @param listener  the object being deregistered.
1480:      * 
1481:      * @see #addProgressListener(ChartProgressListener)
1482:      */
1483:     public void removeProgressListener(ChartProgressListener listener) {
1484:         this.progressListeners.remove(ChartProgressListener.class, listener);
1485:     }
1486: 
1487:     /**
1488:      * Sends a {@link ChartProgressEvent} to all registered listeners.
1489:      *
1490:      * @param event  information about the event that triggered the 
1491:      *               notification.
1492:      */
1493:     protected void notifyListeners(ChartProgressEvent event) {
1494: 
1495:         Object[] listeners = this.progressListeners.getListenerList();
1496:         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1497:             if (listeners[i] == ChartProgressListener.class) {
1498:                 ((ChartProgressListener) listeners[i + 1]).chartProgress(event);
1499:             }
1500:         }
1501: 
1502:     }
1503: 
1504:     /**
1505:      * Receives notification that a chart title has changed, and passes this
1506:      * on to registered listeners.
1507:      *
1508:      * @param event  information about the chart title change.
1509:      */
1510:     public void titleChanged(TitleChangeEvent event) {
1511:         event.setChart(this);
1512:         notifyListeners(event);
1513:     }
1514: 
1515:     /**
1516:      * Receives notification that the plot has changed, and passes this on to
1517:      * registered listeners.
1518:      *
1519:      * @param event  information about the plot change.
1520:      */
1521:     public void plotChanged(PlotChangeEvent event) {
1522:         event.setChart(this);
1523:         notifyListeners(event);
1524:     }
1525: 
1526:     /**
1527:      * Tests this chart for equality with another object.
1528:      *
1529:      * @param obj  the object (<code>null</code> permitted).
1530:      *
1531:      * @return A boolean.
1532:      */
1533:     public boolean equals(Object obj) {
1534:         if (obj == this) {
1535:             return true;
1536:         }
1537:         if (!(obj instanceof JFreeChart)) {
1538:             return false;
1539:         }
1540:         JFreeChart that = (JFreeChart) obj;
1541:         if (!this.renderingHints.equals(that.renderingHints)) {
1542:             return false;   
1543:         }
1544:         if (this.borderVisible != that.borderVisible) {
1545:             return false;   
1546:         }
1547:         if (!ObjectUtilities.equal(this.borderStroke, that.borderStroke)) {
1548:             return false;   
1549:         }
1550:         if (!PaintUtilities.equal(this.borderPaint, that.borderPaint)) {
1551:             return false;   
1552:         }
1553:         if (!this.padding.equals(that.padding)) {
1554:             return false;   
1555:         }
1556:         if (!ObjectUtilities.equal(this.title, that.title)) {
1557:             return false;
1558:         }
1559:         if (!ObjectUtilities.equal(this.subtitles, that.subtitles)) {
1560:             return false;
1561:         }
1562:         if (!ObjectUtilities.equal(this.plot, that.plot)) {
1563:             return false;
1564:         }
1565:         if (!PaintUtilities.equal(
1566:             this.backgroundPaint, that.backgroundPaint
1567:         )) {
1568:             return false;
1569:         }
1570:         if (!ObjectUtilities.equal(this.backgroundImage, 
1571:                 that.backgroundImage)) {
1572:             return false;
1573:         }
1574:         if (this.backgroundImageAlignment != that.backgroundImageAlignment) {
1575:             return false;
1576:         }
1577:         if (this.backgroundImageAlpha != that.backgroundImageAlpha) {
1578:             return false;
1579:         }
1580:         if (this.notify != that.notify) {
1581:             return false;
1582:         }
1583:         return true;
1584:     }
1585: 
1586:     /**
1587:      * Provides serialization support.
1588:      *
1589:      * @param stream  the output stream.
1590:      *
1591:      * @throws IOException  if there is an I/O error.
1592:      */
1593:     private void writeObject(ObjectOutputStream stream) throws IOException {
1594:         stream.defaultWriteObject();
1595:         SerialUtilities.writeStroke(this.borderStroke, stream);
1596:         SerialUtilities.writePaint(this.borderPaint, stream);
1597:         SerialUtilities.writePaint(this.backgroundPaint, stream);
1598:     }
1599: 
1600:     /**
1601:      * Provides serialization support.
1602:      *
1603:      * @param stream  the input stream.
1604:      *
1605:      * @throws IOException  if there is an I/O error.
1606:      * @throws ClassNotFoundException  if there is a classpath problem.
1607:      */
1608:     private void readObject(ObjectInputStream stream) 
1609:         throws IOException, ClassNotFoundException {
1610:         stream.defaultReadObject();
1611:         this.borderStroke = SerialUtilities.readStroke(stream);
1612:         this.borderPaint = SerialUtilities.readPaint(stream);
1613:         this.backgroundPaint = SerialUtilities.readPaint(stream);
1614:         this.progressListeners = new EventListenerList();
1615:         this.changeListeners = new EventListenerList();
1616:         this.renderingHints = new RenderingHints(
1617:                 RenderingHints.KEY_ANTIALIASING, 
1618:                 RenderingHints.VALUE_ANTIALIAS_ON);
1619: 
1620:         // register as a listener with sub-components...
1621:         if (this.title != null) {
1622:             this.title.addChangeListener(this);
1623:         }
1624: 
1625:         for (int i = 0; i < getSubtitleCount(); i++) {
1626:             getSubtitle(i).addChangeListener(this);
1627:         }
1628:         this.plot.addChangeListener(this);
1629:     }
1630: 
1631:     /**
1632:      * Prints information about JFreeChart to standard output.
1633:      *
1634:      * @param args  no arguments are honored.
1635:      */
1636:     public static void main(String[] args) {
1637:         System.out.println(JFreeChart.INFO.toString());
1638:     }
1639: 
1640:     /**
1641:      * Clones the object, and takes care of listeners.
1642:      * Note: caller shall register its own listeners on cloned graph.
1643:      * 
1644:      * @return A clone.
1645:      * 
1646:      * @throws CloneNotSupportedException if the chart is not cloneable.
1647:      */
1648:     public Object clone() throws CloneNotSupportedException {
1649:         JFreeChart chart = (JFreeChart) super.clone();
1650: 
1651:         chart.renderingHints = (RenderingHints) this.renderingHints.clone();
1652:         // private boolean borderVisible;
1653:         // private transient Stroke borderStroke;
1654:         // private transient Paint borderPaint;
1655: 
1656:         if (this.title != null) {
1657:             chart.title = (TextTitle) this.title.clone();
1658:             chart.title.addChangeListener(chart);
1659:         }
1660: 
1661:         chart.subtitles = new ArrayList();
1662:         for (int i = 0; i < getSubtitleCount(); i++) {
1663:             Title subtitle = (Title) getSubtitle(i).clone();
1664:             chart.subtitles.add(subtitle);
1665:             subtitle.addChangeListener(chart);
1666:         }
1667: 
1668:         if (this.plot != null) {
1669:             chart.plot = (Plot) this.plot.clone();
1670:             chart.plot.addChangeListener(chart);
1671:         }
1672: 
1673:         chart.progressListeners = new EventListenerList();
1674:         chart.changeListeners = new EventListenerList();
1675:         return chart;
1676:     }
1677: 
1678: }
1679: 
1680: /**
1681:  * Information about the JFreeChart project.  One instance of this class is 
1682:  * assigned to <code>JFreeChart.INFO<code>.
1683:  */
1684: class JFreeChartInfo extends ProjectInfo {
1685: 
1686:     /** 
1687:      * Default constructor. 
1688:      */
1689:     public JFreeChartInfo() {
1690: 
1691:         // get a locale-specific resource bundle...
1692:         String baseResourceClass 
1693:             = "org.jfree.chart.resources.JFreeChartResources";
1694:         ResourceBundle resources = ResourceBundle.getBundle(baseResourceClass);
1695: 
1696:         setName(resources.getString("project.name"));
1697:         setVersion(resources.getString("project.version"));
1698:         setInfo(resources.getString("project.info"));
1699:         setCopyright(resources.getString("project.copyright"));
1700:         setLogo(null);  // load only when required
1701:         setLicenceName("LGPL");
1702:         setLicenceText(Licences.getInstance().getLGPL());
1703: 
1704:         setContributors(Arrays.asList(
1705:             new Contributor[]{
1706:                 new Contributor("Eric Alexander", "-"),
1707:                 new Contributor(
1708:                     "Richard Atkinson", "richard_c_atkinson@ntlworld.com"
1709:                 ),
1710:                 new Contributor("David Basten", "-"),
1711:                 new Contributor("David Berry", "-"),
1712:                 new Contributor("Anthony Boulestreau", "-"),
1713:                 new Contributor("Jeremy Bowman", "-"),
1714:                 new Contributor("Nicolas Brodu", "-"),
1715:                 new Contributor("David Browning", "-"),
1716:                 new Contributor("Soren Caspersen", "-"),
1717:                 new Contributor("Chuanhao Chiu", "-"),
1718:                 new Contributor("Brian Cole", "-"),
1719:                 new Contributor("Pascal Collet", "-"),
1720:                 new Contributor("Martin Cordova", "-"),
1721:                 new Contributor("Paolo Cova", "-"),
1722:                 new Contributor("Mike Duffy", "-"),
1723:                 new Contributor("Don Elliott", "-"),
1724:                 new Contributor("Jonathan Gabbai", "-"),
1725:                 new Contributor("David Gilbert", 
1726:                         "david.gilbert@object-refinery.com"),
1727:                 new Contributor("Serge V. Grachov", "-"),
1728:                 new Contributor("Daniel Gredler", "-"),
1729:                 new Contributor("Hans-Jurgen Greiner", "-"),
1730:                 new Contributor("Joao Guilherme Del Valle", "-"),
1731:                 new Contributor("Aiman Han", "-"),
1732:                 new Contributor("Cameron Hayne", "-"),
1733:                 new Contributor("Jon Iles", "-"),
1734:                 new Contributor("Wolfgang Irler", "-"),
1735:                 new Contributor("Sergei Ivanov", "-"),
1736:                 new Contributor("Adriaan Joubert", "-"),
1737:                 new Contributor("Darren Jung", "-"),
1738:                 new Contributor("Xun Kang", "-"),
1739:                 new Contributor("Bill Kelemen", "-"),
1740:                 new Contributor("Norbert Kiesel", "-"),
1741:                 new Contributor("Gideon Krause", "-"),
1742:                 new Contributor("Pierre-Marie Le Biot", "-"),
1743:                 new Contributor("Arnaud Lelievre", "-"),
1744:                 new Contributor("Wolfgang Lenhard", "-"),
1745:                 new Contributor("David Li", "-"),
1746:                 new Contributor("Yan Liu", "-"),
1747:                 new Contributor("Tin Luu", "-"),
1748:                 new Contributor("Craig MacFarlane", "-"),
1749:                 new Contributor("Achilleus Mantzios", "-"),
1750:                 new Contributor("Thomas Meier", "-"),
1751:                 new Contributor("Jim Moore", "-"),
1752:                 new Contributor("Jonathan Nash", "-"),
1753:                 new Contributor("Barak Naveh", "-"),
1754:                 new Contributor("David M. O'Donnell", "-"),
1755:                 new Contributor("Krzysztof Paz", "-"),
1756:                 new Contributor("Tomer Peretz", "-"),
1757:                 new Contributor("Andrzej Porebski", "-"),
1758:                 new Contributor("Xavier Poinsard", "-"),
1759:                 new Contributor("Viktor Rajewski", "-"),
1760:                 new Contributor("Eduardo Ramalho", "-"),
1761:                 new Contributor("Michael Rauch", "-"),
1762:                 new Contributor("Cameron Riley", "-"),
1763:                 new Contributor("Klaus Rheinwald", "-"),
1764:                 new Contributor("Dan Rivett", "d.rivett@ukonline.co.uk"),
1765:                 new Contributor("Scott Sams", "-"),
1766:                 new Contributor("Michel Santos", "-"),
1767:                 new Contributor("Thierry Saura", "-"),
1768:                 new Contributor("Andreas Schneider", "-"),
1769:                 new Contributor("Jean-Luc SCHWAB", "-"),
1770:                 new Contributor("Bryan Scott", "-"),
1771:                 new Contributor("Tobias Selb", "-"),
1772:                 new Contributor("Mofeed Shahin", "-"),
1773:                 new Contributor("Pady Srinivasan", "-"),
1774:                 new Contributor("Greg Steckman", "-"),
1775:                 new Contributor("Roger Studner", "-"),
1776:                 new Contributor("Irv Thomae", "-"),
1777:                 new Contributor("Eric Thomas", "-"),
1778:                 new Contributor("Rich Unger", "-"),
1779:                 new Contributor("Daniel van Enckevort", "-"),
1780:                 new Contributor("Laurence Vanhelsuwe", "-"),
1781:                 new Contributor("Sylvain Vieujot", "-"),
1782:                 new Contributor("Mark Watson", "www.markwatson.com"),
1783:                 new Contributor("Alex Weber", "-"),
1784:                 new Contributor("Matthew Wright", "-"),
1785:                 new Contributor("Benoit Xhenseval", "-"),
1786:                 new Contributor("Christian W. Zuckschwerdt", 
1787:                         "Christian.Zuckschwerdt@Informatik.Uni-Oldenburg.de"),
1788:                 new Contributor("Hari", "-"),
1789:                 new Contributor("Sam (oldman)", "-"),
1790:             }
1791:         ));
1792: 
1793:         addLibrary(JCommon.INFO);
1794: 
1795:     }
1796: 
1797:     /**
1798:      * Returns the JFreeChart logo (a picture of a gorilla).
1799:      *
1800:      * @return The JFreeChart logo.
1801:      */
1802:     public Image getLogo() {
1803: 
1804:         Image logo = super.getLogo();
1805:         if (logo == null) {
1806:             URL imageURL = this.getClass().getClassLoader().getResource(
1807:                     "org/jfree/chart/gorilla.jpg");
1808:             if (imageURL != null) {
1809:                 ImageIcon temp = new ImageIcon(imageURL);  
1810:                     // use ImageIcon because it waits for the image to load...
1811:                 logo = temp.getImage();
1812:                 setLogo(logo);
1813:             }
1814:         }
1815:         return logo;
1816: 
1817:     }
1818: 
1819: }
Overview Package Class Use Source Tree Index Deprecated About

 

posted on 2013-05-31 05:39  Step-BY-Step  阅读(436)  评论(0编辑  收藏  举报

导航