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

Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » [javadoc | source]