Source for org.jfree.chart.renderer.xy.AbstractXYItemRenderer

   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:  * AbstractXYItemRenderer.java
  29:  * ---------------------------
  30:  * (C) Copyright 2002-2008, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   Richard Atkinson;
  34:  *                   Focus Computer Services Limited;
  35:  *                   Tim Bardzil;
  36:  *                   Sergei Ivanov;
  37:  *
  38:  * Changes:
  39:  * --------
  40:  * 15-Mar-2002 : Version 1 (DG);
  41:  * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in
  42:  *               the XYItemRenderer interface (DG);
  43:  * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image
  44:  *               maps (RA);
  45:  * 20-Aug-2002 : Added property change events for the tooltip and URL
  46:  *               generators (DG);
  47:  * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG);
  48:  * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG);
  49:  * 18-Nov-2002 : Added methods for drawing grid lines (DG);
  50:  * 17-Jan-2003 : Moved plot classes into a separate package (DG);
  51:  * 25-Mar-2003 : Implemented Serializable (DG);
  52:  * 01-May-2003 : Modified initialise() return type and drawItem() method
  53:  *               signature (DG);
  54:  * 15-May-2003 : Modified to take into account the plot orientation (DG);
  55:  * 21-May-2003 : Added labels to markers (DG);
  56:  * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
  57:  *               Services Ltd) (DG);
  58:  * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA);
  59:  * 31-Jul-2003 : Deprecated all but the default constructor (DG);
  60:  * 13-Aug-2003 : Implemented Cloneable (DG);
  61:  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  62:  * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
  63:  * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
  64:  * 11-Feb-2004 : Updated labelling for markers (DG);
  65:  * 25-Feb-2004 : Added updateCrosshairValues() method.  Moved deprecated code
  66:  *               to bottom of source file (DG);
  67:  * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method
  68:  *               - thanks to Tim Bardzil (DG);
  69:  * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis
  70:  *               range (DG);
  71:  * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG);
  72:  * 26-Aug-2004 : Added the addEntity() method (DG);
  73:  * 29-Sep-2004 : Added annotation support (with layers) (DG);
  74:  * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
  75:  *               TextUtilities (DG);
  76:  * 06-Oct-2004 : Added findDomainBounds() method and renamed
  77:  *               getRangeExtent() --> findRangeBounds() (DG);
  78:  * 07-Jan-2005 : Removed deprecated code (DG);
  79:  * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG);
  80:  * 24-Feb-2005 : Added getLegendItems() method (DG);
  81:  * 08-Mar-2005 : Fixed positioning of marker labels (DG);
  82:  * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and
  83:  *               added generators for legend labels, tooltips and URLs (DG);
  84:  * 01-Jun-2005 : Handle one dimension of the marker label adjustment
  85:  *               automatically (DG);
  86:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  87:  * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
  88:  * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei
  89:  *               Ivanov) (DG);
  90:  * 24-Oct-2006 : Added code to draw outlines for interval markers (DG);
  91:  * 24-Nov-2006 : Fixed cloning for legend item generators (DG);
  92:  * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into
  93:  *               account multiple axis plots (see bug 1086307) (DG);
  94:  * 20-Feb-2007 : Fixed equals() method implementation (DG);
  95:  * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to
  96:  *               Sergei Ivanov) (DG);
  97:  * 22-Mar-2007 : Modified the tool tip generator look up (DG);
  98:  * 23-Mar-2007 : Added drawDomainLine() method (DG);
  99:  * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
 100:  *               itemLabelGenerator and toolTipGenerator override fields (DG);
 101:  * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
 102:  * 12-Nov-2007 : Fixed domain and range band drawing methods (DG);
 103:  * 07-Apr-2008 : Minor API doc update (DG);
 104:  * 14-May-2008 : Updated addEntity() method to take plot orientation into
 105:  *               account when the incoming area is null (DG);
 106:  * 02-Jun-2008 : Added isPointInRect() method (DG);
 107:  *
 108:  */
 109: 
 110: package org.jfree.chart.renderer.xy;
 111: 
 112: import java.awt.AlphaComposite;
 113: import java.awt.Composite;
 114: import java.awt.Font;
 115: import java.awt.GradientPaint;
 116: import java.awt.Graphics2D;
 117: import java.awt.Paint;
 118: import java.awt.Shape;
 119: import java.awt.Stroke;
 120: import java.awt.geom.Ellipse2D;
 121: import java.awt.geom.Line2D;
 122: import java.awt.geom.Point2D;
 123: import java.awt.geom.Rectangle2D;
 124: import java.io.Serializable;
 125: import java.util.Iterator;
 126: import java.util.List;
 127: 
 128: import org.jfree.chart.LegendItem;
 129: import org.jfree.chart.LegendItemCollection;
 130: import org.jfree.chart.annotations.XYAnnotation;
 131: import org.jfree.chart.axis.ValueAxis;
 132: import org.jfree.chart.entity.EntityCollection;
 133: import org.jfree.chart.entity.XYItemEntity;
 134: import org.jfree.chart.event.RendererChangeEvent;
 135: import org.jfree.chart.labels.ItemLabelPosition;
 136: import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
 137: import org.jfree.chart.labels.XYItemLabelGenerator;
 138: import org.jfree.chart.labels.XYSeriesLabelGenerator;
 139: import org.jfree.chart.labels.XYToolTipGenerator;
 140: import org.jfree.chart.plot.CrosshairState;
 141: import org.jfree.chart.plot.DrawingSupplier;
 142: import org.jfree.chart.plot.IntervalMarker;
 143: import org.jfree.chart.plot.Marker;
 144: import org.jfree.chart.plot.Plot;
 145: import org.jfree.chart.plot.PlotOrientation;
 146: import org.jfree.chart.plot.PlotRenderingInfo;
 147: import org.jfree.chart.plot.ValueMarker;
 148: import org.jfree.chart.plot.XYPlot;
 149: import org.jfree.chart.renderer.AbstractRenderer;
 150: import org.jfree.chart.urls.XYURLGenerator;
 151: import org.jfree.data.Range;
 152: import org.jfree.data.general.DatasetUtilities;
 153: import org.jfree.data.xy.XYDataset;
 154: import org.jfree.text.TextUtilities;
 155: import org.jfree.ui.GradientPaintTransformer;
 156: import org.jfree.ui.Layer;
 157: import org.jfree.ui.LengthAdjustmentType;
 158: import org.jfree.ui.RectangleAnchor;
 159: import org.jfree.ui.RectangleInsets;
 160: import org.jfree.util.ObjectList;
 161: import org.jfree.util.ObjectUtilities;
 162: import org.jfree.util.PublicCloneable;
 163: 
 164: /**
 165:  * A base class that can be used to create new {@link XYItemRenderer}
 166:  * implementations.
 167:  */
 168: public abstract class AbstractXYItemRenderer extends AbstractRenderer
 169:         implements XYItemRenderer, Cloneable, Serializable {
 170: 
 171:     /** For serialization. */
 172:     private static final long serialVersionUID = 8019124836026607990L;
 173: 
 174:     /** The plot. */
 175:     private XYPlot plot;
 176: 
 177:     /**
 178:      * The item label generator for ALL series.
 179:      *
 180:      * @deprecated This field is redundant, use itemLabelGeneratorList and
 181:      *     baseItemLabelGenerator instead.  Deprecated as of version 1.0.6.
 182:      */
 183:     private XYItemLabelGenerator itemLabelGenerator;
 184: 
 185:     /** A list of item label generators (one per series). */
 186:     private ObjectList itemLabelGeneratorList;
 187: 
 188:     /** The base item label generator. */
 189:     private XYItemLabelGenerator baseItemLabelGenerator;
 190: 
 191:     /**
 192:      * The tool tip generator for ALL series.
 193:      *
 194:      * @deprecated This field is redundant, use tooltipGeneratorList and
 195:      *     baseToolTipGenerator instead.  Deprecated as of version 1.0.6.
 196:      */
 197:     private XYToolTipGenerator toolTipGenerator;
 198: 
 199:     /** A list of tool tip generators (one per series). */
 200:     private ObjectList toolTipGeneratorList;
 201: 
 202:     /** The base tool tip generator. */
 203:     private XYToolTipGenerator baseToolTipGenerator;
 204: 
 205:     /** The URL text generator. */
 206:     private XYURLGenerator urlGenerator;
 207: 
 208:     /**
 209:      * Annotations to be drawn in the background layer ('underneath' the data
 210:      * items).
 211:      */
 212:     private List backgroundAnnotations;
 213: 
 214:     /**
 215:      * Annotations to be drawn in the foreground layer ('on top' of the data
 216:      * items).
 217:      */
 218:     private List foregroundAnnotations;
 219: 
 220:     /** The default radius for the entity 'hotspot' */
 221:     private int defaultEntityRadius;
 222: 
 223:     /** The legend item label generator. */
 224:     private XYSeriesLabelGenerator legendItemLabelGenerator;
 225: 
 226:     /** The legend item tool tip generator. */
 227:     private XYSeriesLabelGenerator legendItemToolTipGenerator;
 228: 
 229:     /** The legend item URL generator. */
 230:     private XYSeriesLabelGenerator legendItemURLGenerator;
 231: 
 232:     /**
 233:      * Creates a renderer where the tooltip generator and the URL generator are
 234:      * both <code>null</code>.
 235:      */
 236:     protected AbstractXYItemRenderer() {
 237:         super();
 238:         this.itemLabelGenerator = null;
 239:         this.itemLabelGeneratorList = new ObjectList();
 240:         this.toolTipGenerator = null;
 241:         this.toolTipGeneratorList = new ObjectList();
 242:         this.urlGenerator = null;
 243:         this.backgroundAnnotations = new java.util.ArrayList();
 244:         this.foregroundAnnotations = new java.util.ArrayList();
 245:         this.defaultEntityRadius = 3;
 246:         this.legendItemLabelGenerator = new StandardXYSeriesLabelGenerator(
 247:                 "{0}");
 248:     }
 249: 
 250:     /**
 251:      * Returns the number of passes through the data that the renderer requires
 252:      * in order to draw the chart.  Most charts will require a single pass, but
 253:      * some require two passes.
 254:      *
 255:      * @return The pass count.
 256:      */
 257:     public int getPassCount() {
 258:         return 1;
 259:     }
 260: 
 261:     /**
 262:      * Returns the plot that the renderer is assigned to.
 263:      *
 264:      * @return The plot (possibly <code>null</code>).
 265:      */
 266:     public XYPlot getPlot() {
 267:         return this.plot;
 268:     }
 269: 
 270:     /**
 271:      * Sets the plot that the renderer is assigned to.
 272:      *
 273:      * @param plot  the plot (<code>null</code> permitted).
 274:      */
 275:     public void setPlot(XYPlot plot) {
 276:         this.plot = plot;
 277:     }
 278: 
 279:     /**
 280:      * Initialises the renderer and returns a state object that should be
 281:      * passed to all subsequent calls to the drawItem() method.
 282:      * <P>
 283:      * This method will be called before the first item is rendered, giving the
 284:      * renderer an opportunity to initialise any state information it wants to
 285:      * maintain.  The renderer can do nothing if it chooses.
 286:      *
 287:      * @param g2  the graphics device.
 288:      * @param dataArea  the area inside the axes.
 289:      * @param plot  the plot.
 290:      * @param data  the data.
 291:      * @param info  an optional info collection object to return data back to
 292:      *              the caller.
 293:      *
 294:      * @return The renderer state (never <code>null</code>).
 295:      */
 296:     public XYItemRendererState initialise(Graphics2D g2,
 297:                                           Rectangle2D dataArea,
 298:                                           XYPlot plot,
 299:                                           XYDataset data,
 300:                                           PlotRenderingInfo info) {
 301: 
 302:         XYItemRendererState state = new XYItemRendererState(info);
 303:         return state;
 304: 
 305:     }
 306: 
 307:     // ITEM LABEL GENERATOR
 308: 
 309:     /**
 310:      * Returns the label generator for a data item.  This implementation simply
 311:      * passes control to the {@link #getSeriesItemLabelGenerator(int)} method.
 312:      * If, for some reason, you want a different generator for individual
 313:      * items, you can override this method.
 314:      *
 315:      * @param series  the series index (zero based).
 316:      * @param item  the item index (zero based).
 317:      *
 318:      * @return The generator (possibly <code>null</code>).
 319:      */
 320:     public XYItemLabelGenerator getItemLabelGenerator(int series, int item) {
 321:         // return the generator for ALL series, if there is one...
 322:         if (this.itemLabelGenerator != null) {
 323:             return this.itemLabelGenerator;
 324:         }
 325: 
 326:         // otherwise look up the generator table
 327:         XYItemLabelGenerator generator
 328:             = (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
 329:         if (generator == null) {
 330:             generator = this.baseItemLabelGenerator;
 331:         }
 332:         return generator;
 333:     }
 334: 
 335:     /**
 336:      * Returns the item label generator for a series.
 337:      *
 338:      * @param series  the series index (zero based).
 339:      *
 340:      * @return The generator (possibly <code>null</code>).
 341:      */
 342:     public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) {
 343:         return (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
 344:     }
 345: 
 346:     /**
 347:      * Returns the item label generator override.
 348:      *
 349:      * @return The generator (possibly <code>null</code>).
 350:      *
 351:      * @since 1.0.5
 352:      *
 353:      * @see #setItemLabelGenerator(XYItemLabelGenerator)
 354:      *
 355:      * @deprecated As of version 1.0.6, this override setting should not be
 356:      *     used.  You can use the base setting instead
 357:      *     ({@link #getBaseItemLabelGenerator()}).
 358:      */
 359:     public XYItemLabelGenerator getItemLabelGenerator() {
 360:         return this.itemLabelGenerator;
 361:     }
 362: 
 363:     /**
 364:      * Sets the item label generator for ALL series and sends a
 365:      * {@link RendererChangeEvent} to all registered listeners.
 366:      *
 367:      * @param generator  the generator (<code>null</code> permitted).
 368:      *
 369:      * @see #getItemLabelGenerator()
 370:      *
 371:      * @deprecated As of version 1.0.6, this override setting should not be
 372:      *     used.  You can use the base setting instead
 373:      *     ({@link #setBaseItemLabelGenerator(XYItemLabelGenerator)}).
 374:      */
 375:     public void setItemLabelGenerator(XYItemLabelGenerator generator) {
 376:         this.itemLabelGenerator = generator;
 377:         fireChangeEvent();
 378:     }
 379: 
 380:     /**
 381:      * Sets the item label generator for a series and sends a
 382:      * {@link RendererChangeEvent} to all registered listeners.
 383:      *
 384:      * @param series  the series index (zero based).
 385:      * @param generator  the generator (<code>null</code> permitted).
 386:      */
 387:     public void setSeriesItemLabelGenerator(int series,
 388:                                             XYItemLabelGenerator generator) {
 389:         this.itemLabelGeneratorList.set(series, generator);
 390:         fireChangeEvent();
 391:     }
 392: 
 393:     /**
 394:      * Returns the base item label generator.
 395:      *
 396:      * @return The generator (possibly <code>null</code>).
 397:      */
 398:     public XYItemLabelGenerator getBaseItemLabelGenerator() {
 399:         return this.baseItemLabelGenerator;
 400:     }
 401: 
 402:     /**
 403:      * Sets the base item label generator and sends a
 404:      * {@link RendererChangeEvent} to all registered listeners.
 405:      *
 406:      * @param generator  the generator (<code>null</code> permitted).
 407:      */
 408:     public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) {
 409:         this.baseItemLabelGenerator = generator;
 410:         fireChangeEvent();
 411:     }
 412: 
 413:     // TOOL TIP GENERATOR
 414: 
 415:     /**
 416:      * Returns the tool tip generator for a data item.  If, for some reason,
 417:      * you want a different generator for individual items, you can override
 418:      * this method.
 419:      *
 420:      * @param series  the series index (zero based).
 421:      * @param item  the item index (zero based).
 422:      *
 423:      * @return The generator (possibly <code>null</code>).
 424:      */
 425:     public XYToolTipGenerator getToolTipGenerator(int series, int item) {
 426:         // return the generator for ALL series, if there is one...
 427:         if (this.toolTipGenerator != null) {
 428:             return this.toolTipGenerator;
 429:         }
 430: 
 431:         // otherwise look up the generator table
 432:         XYToolTipGenerator generator
 433:                 = (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
 434:         if (generator == null) {
 435:             generator = this.baseToolTipGenerator;
 436:         }
 437:         return generator;
 438:     }
 439: 
 440:     /**
 441:      * Returns the override tool tip generator.
 442:      *
 443:      * @return The tool tip generator (possible <code>null</code>).
 444:      *
 445:      * @since 1.0.5
 446:      *
 447:      * @see #setToolTipGenerator(XYToolTipGenerator)
 448:      *
 449:      * @deprecated As of version 1.0.6, this override setting should not be
 450:      *     used.  You can use the base setting instead
 451:      *     ({@link #getBaseToolTipGenerator()}).
 452:      */
 453:     public XYToolTipGenerator getToolTipGenerator() {
 454:         return this.toolTipGenerator;
 455:     }
 456: 
 457:     /**
 458:      * Sets the tool tip generator for ALL series and sends a
 459:      * {@link RendererChangeEvent} to all registered listeners.
 460:      *
 461:      * @param generator  the generator (<code>null</code> permitted).
 462:      *
 463:      * @see #getToolTipGenerator()
 464:      *
 465:      * @deprecated As of version 1.0.6, this override setting should not be
 466:      *     used.  You can use the base setting instead
 467:      *     ({@link #setBaseToolTipGenerator(XYToolTipGenerator)}).
 468:      */
 469:     public void setToolTipGenerator(XYToolTipGenerator generator) {
 470:         this.toolTipGenerator = generator;
 471:         fireChangeEvent();
 472:     }
 473: 
 474:     /**
 475:      * Returns the tool tip generator for a series.
 476:      *
 477:      * @param series  the series index (zero based).
 478:      *
 479:      * @return The generator (possibly <code>null</code>).
 480:      */
 481:     public XYToolTipGenerator getSeriesToolTipGenerator(int series) {
 482:         return (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
 483:     }
 484: 
 485:     /**
 486:      * Sets the tool tip generator for a series and sends a
 487:      * {@link RendererChangeEvent} to all registered listeners.
 488:      *
 489:      * @param series  the series index (zero based).
 490:      * @param generator  the generator (<code>null</code> permitted).
 491:      */
 492:     public void setSeriesToolTipGenerator(int series,
 493:                                           XYToolTipGenerator generator) {
 494:         this.toolTipGeneratorList.set(series, generator);
 495:         fireChangeEvent();
 496:     }
 497: 
 498:     /**
 499:      * Returns the base tool tip generator.
 500:      *
 501:      * @return The generator (possibly <code>null</code>).
 502:      *
 503:      * @see #setBaseToolTipGenerator(XYToolTipGenerator)
 504:      */
 505:     public XYToolTipGenerator getBaseToolTipGenerator() {
 506:         return this.baseToolTipGenerator;
 507:     }
 508: 
 509:     /**
 510:      * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
 511:      * to all registered listeners.
 512:      *
 513:      * @param generator  the generator (<code>null</code> permitted).
 514:      *
 515:      * @see #getBaseToolTipGenerator()
 516:      */
 517:     public void setBaseToolTipGenerator(XYToolTipGenerator generator) {
 518:         this.baseToolTipGenerator = generator;
 519:         fireChangeEvent();
 520:     }
 521: 
 522:     // URL GENERATOR
 523: 
 524:     /**
 525:      * Returns the URL generator for HTML image maps.
 526:      *
 527:      * @return The URL generator (possibly <code>null</code>).
 528:      */
 529:     public XYURLGenerator getURLGenerator() {
 530:         return this.urlGenerator;
 531:     }
 532: 
 533:     /**
 534:      * Sets the URL generator for HTML image maps and sends a
 535:      * {@link RendererChangeEvent} to all registered listeners.
 536:      *
 537:      * @param urlGenerator  the URL generator (<code>null</code> permitted).
 538:      */
 539:     public void setURLGenerator(XYURLGenerator urlGenerator) {
 540:         this.urlGenerator = urlGenerator;
 541:         fireChangeEvent();
 542:     }
 543: 
 544:     /**
 545:      * Adds an annotation and sends a {@link RendererChangeEvent} to all
 546:      * registered listeners.  The annotation is added to the foreground
 547:      * layer.
 548:      *
 549:      * @param annotation  the annotation (<code>null</code> not permitted).
 550:      */
 551:     public void addAnnotation(XYAnnotation annotation) {
 552:         // defer argument checking
 553:         addAnnotation(annotation, Layer.FOREGROUND);
 554:     }
 555: 
 556:     /**
 557:      * Adds an annotation to the specified layer and sends a
 558:      * {@link RendererChangeEvent} to all registered listeners.
 559:      *
 560:      * @param annotation  the annotation (<code>null</code> not permitted).
 561:      * @param layer  the layer (<code>null</code> not permitted).
 562:      */
 563:     public void addAnnotation(XYAnnotation annotation, Layer layer) {
 564:         if (annotation == null) {
 565:             throw new IllegalArgumentException("Null 'annotation' argument.");
 566:         }
 567:         if (layer.equals(Layer.FOREGROUND)) {
 568:             this.foregroundAnnotations.add(annotation);
 569:             fireChangeEvent();
 570:         }
 571:         else if (layer.equals(Layer.BACKGROUND)) {
 572:             this.backgroundAnnotations.add(annotation);
 573:             fireChangeEvent();
 574:         }
 575:         else {
 576:             // should never get here
 577:             throw new RuntimeException("Unknown layer.");
 578:         }
 579:     }
 580:     /**
 581:      * Removes the specified annotation and sends a {@link RendererChangeEvent}
 582:      * to all registered listeners.
 583:      *
 584:      * @param annotation  the annotation to remove (<code>null</code> not
 585:      *                    permitted).
 586:      *
 587:      * @return A boolean to indicate whether or not the annotation was
 588:      *         successfully removed.
 589:      */
 590:     public boolean removeAnnotation(XYAnnotation annotation) {
 591:         boolean removed = this.foregroundAnnotations.remove(annotation);
 592:         removed = removed & this.backgroundAnnotations.remove(annotation);
 593:         fireChangeEvent();
 594:         return removed;
 595:     }
 596: 
 597:     /**
 598:      * Removes all annotations and sends a {@link RendererChangeEvent}
 599:      * to all registered listeners.
 600:      */
 601:     public void removeAnnotations() {
 602:         this.foregroundAnnotations.clear();
 603:         this.backgroundAnnotations.clear();
 604:         fireChangeEvent();
 605:     }
 606: 
 607:     /**
 608:      * Returns the radius of the circle used for the default entity area
 609:      * when no area is specified.
 610:      *
 611:      * @return A radius.
 612:      */
 613:     public int getDefaultEntityRadius() {
 614:         return this.defaultEntityRadius;
 615:     }
 616: 
 617:     /**
 618:      * Sets the radius of the circle used for the default entity area
 619:      * when no area is specified.
 620:      *
 621:      * @param radius  the radius.
 622:      */
 623:     public void setDefaultEntityRadius(int radius) {
 624:         this.defaultEntityRadius = radius;
 625:     }
 626: 
 627:     /**
 628:      * Returns the legend item label generator.
 629:      *
 630:      * @return The label generator (never <code>null</code>).
 631:      *
 632:      * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator)
 633:      */
 634:     public XYSeriesLabelGenerator getLegendItemLabelGenerator() {
 635:         return this.legendItemLabelGenerator;
 636:     }
 637: 
 638:     /**
 639:      * Sets the legend item label generator and sends a
 640:      * {@link RendererChangeEvent} to all registered listeners.
 641:      *
 642:      * @param generator  the generator (<code>null</code> not permitted).
 643:      *
 644:      * @see #getLegendItemLabelGenerator()
 645:      */
 646:     public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) {
 647:         if (generator == null) {
 648:             throw new IllegalArgumentException("Null 'generator' argument.");
 649:         }
 650:         this.legendItemLabelGenerator = generator;
 651:         fireChangeEvent();
 652:     }
 653: 
 654:     /**
 655:      * Returns the legend item tool tip generator.
 656:      *
 657:      * @return The tool tip generator (possibly <code>null</code>).
 658:      *
 659:      * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator)
 660:      */
 661:     public XYSeriesLabelGenerator getLegendItemToolTipGenerator() {
 662:         return this.legendItemToolTipGenerator;
 663:     }
 664: 
 665:     /**
 666:      * Sets the legend item tool tip generator and sends a
 667:      * {@link RendererChangeEvent} to all registered listeners.
 668:      *
 669:      * @param generator  the generator (<code>null</code> permitted).
 670:      *
 671:      * @see #getLegendItemToolTipGenerator()
 672:      */
 673:     public void setLegendItemToolTipGenerator(
 674:             XYSeriesLabelGenerator generator) {
 675:         this.legendItemToolTipGenerator = generator;
 676:         fireChangeEvent();
 677:     }
 678: 
 679:     /**
 680:      * Returns the legend item URL generator.
 681:      *
 682:      * @return The URL generator (possibly <code>null</code>).
 683:      *
 684:      * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator)
 685:      */
 686:     public XYSeriesLabelGenerator getLegendItemURLGenerator() {
 687:         return this.legendItemURLGenerator;
 688:     }
 689: 
 690:     /**
 691:      * Sets the legend item URL generator and sends a
 692:      * {@link RendererChangeEvent} to all registered listeners.
 693:      *
 694:      * @param generator  the generator (<code>null</code> permitted).
 695:      *
 696:      * @see #getLegendItemURLGenerator()
 697:      */
 698:     public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) {
 699:         this.legendItemURLGenerator = generator;
 700:         fireChangeEvent();
 701:     }
 702: 
 703:     /**
 704:      * Returns the lower and upper bounds (range) of the x-values in the
 705:      * specified dataset.
 706:      *
 707:      * @param dataset  the dataset (<code>null</code> permitted).
 708:      *
 709:      * @return The range (<code>null</code> if the dataset is <code>null</code>
 710:      *         or empty).
 711:      */
 712:     public Range findDomainBounds(XYDataset dataset) {
 713:         if (dataset != null) {
 714:             return DatasetUtilities.findDomainBounds(dataset, false);
 715:         }
 716:         else {
 717:             return null;
 718:         }
 719:     }
 720: 
 721:     /**
 722:      * Returns the range of values the renderer requires to display all the
 723:      * items from the specified dataset.
 724:      *
 725:      * @param dataset  the dataset (<code>null</code> permitted).
 726:      *
 727:      * @return The range (<code>null</code> if the dataset is <code>null</code>
 728:      *         or empty).
 729:      */
 730:     public Range findRangeBounds(XYDataset dataset) {
 731:         if (dataset != null) {
 732:             return DatasetUtilities.findRangeBounds(dataset, false);
 733:         }
 734:         else {
 735:             return null;
 736:         }
 737:     }
 738: 
 739:     /**
 740:      * Returns a (possibly empty) collection of legend items for the series
 741:      * that this renderer is responsible for drawing.
 742:      *
 743:      * @return The legend item collection (never <code>null</code>).
 744:      */
 745:     public LegendItemCollection getLegendItems() {
 746:         if (this.plot == null) {
 747:             return new LegendItemCollection();
 748:         }
 749:         LegendItemCollection result = new LegendItemCollection();
 750:         int index = this.plot.getIndexOf(this);
 751:         XYDataset dataset = this.plot.getDataset(index);
 752:         if (dataset != null) {
 753:             int seriesCount = dataset.getSeriesCount();
 754:             for (int i = 0; i < seriesCount; i++) {
 755:                 if (isSeriesVisibleInLegend(i)) {
 756:                     LegendItem item = getLegendItem(index, i);
 757:                     if (item != null) {
 758:                         result.add(item);
 759:                     }
 760:                 }
 761:             }
 762: 
 763:         }
 764:         return result;
 765:     }
 766: 
 767:     /**
 768:      * Returns a default legend item for the specified series.  Subclasses
 769:      * should override this method to generate customised items.
 770:      *
 771:      * @param datasetIndex  the dataset index (zero-based).
 772:      * @param series  the series index (zero-based).
 773:      *
 774:      * @return A legend item for the series.
 775:      */
 776:     public LegendItem getLegendItem(int datasetIndex, int series) {
 777:         LegendItem result = null;
 778:         XYPlot xyplot = getPlot();
 779:         if (xyplot != null) {
 780:             XYDataset dataset = xyplot.getDataset(datasetIndex);
 781:             if (dataset != null) {
 782:                 String label = this.legendItemLabelGenerator.generateLabel(
 783:                         dataset, series);
 784:                 String description = label;
 785:                 String toolTipText = null;
 786:                 if (getLegendItemToolTipGenerator() != null) {
 787:                     toolTipText = getLegendItemToolTipGenerator().generateLabel(
 788:                             dataset, series);
 789:                 }
 790:                 String urlText = null;
 791:                 if (getLegendItemURLGenerator() != null) {
 792:                     urlText = getLegendItemURLGenerator().generateLabel(
 793:                             dataset, series);
 794:                 }
 795:                 Shape shape = lookupSeriesShape(series);
 796:                 Paint paint = lookupSeriesPaint(series);
 797:                 Paint outlinePaint = lookupSeriesOutlinePaint(series);
 798:                 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
 799:                 result = new LegendItem(label, description, toolTipText,
 800:                         urlText, shape, paint, outlineStroke, outlinePaint);
 801:                 result.setSeriesKey(dataset.getSeriesKey(series));
 802:                 result.setSeriesIndex(series);
 803:                 result.setDataset(dataset);
 804:                 result.setDatasetIndex(datasetIndex);
 805:             }
 806:         }
 807:         return result;
 808:     }
 809: 
 810:     /**
 811:      * Fills a band between two values on the axis.  This can be used to color
 812:      * bands between the grid lines.
 813:      *
 814:      * @param g2  the graphics device.
 815:      * @param plot  the plot.
 816:      * @param axis  the domain axis.
 817:      * @param dataArea  the data area.
 818:      * @param start  the start value.
 819:      * @param end  the end value.
 820:      */
 821:     public void fillDomainGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
 822:             Rectangle2D dataArea, double start, double end) {
 823: 
 824:         double x1 = axis.valueToJava2D(start, dataArea,
 825:                 plot.getDomainAxisEdge());
 826:         double x2 = axis.valueToJava2D(end, dataArea,
 827:                 plot.getDomainAxisEdge());
 828:         Rectangle2D band;
 829:         if (plot.getOrientation() == PlotOrientation.VERTICAL) {
 830:             band = new Rectangle2D.Double(Math.min(x1, x2), dataArea.getMinY(),
 831:                     Math.abs(x2 - x1), dataArea.getWidth());
 832:         }
 833:         else {
 834:             band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(x1, x2),
 835:                     dataArea.getWidth(), Math.abs(x2 - x1));
 836:         }
 837:         Paint paint = plot.getDomainTickBandPaint();
 838: 
 839:         if (paint != null) {
 840:             g2.setPaint(paint);
 841:             g2.fill(band);
 842:         }
 843: 
 844:     }
 845: 
 846:     /**
 847:      * Fills a band between two values on the range axis.  This can be used to
 848:      * color bands between the grid lines.
 849:      *
 850:      * @param g2  the graphics device.
 851:      * @param plot  the plot.
 852:      * @param axis  the range axis.
 853:      * @param dataArea  the data area.
 854:      * @param start  the start value.
 855:      * @param end  the end value.
 856:      */
 857:     public void fillRangeGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
 858:             Rectangle2D dataArea, double start, double end) {
 859: 
 860:         double y1 = axis.valueToJava2D(start, dataArea,
 861:                 plot.getRangeAxisEdge());
 862:         double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge());
 863:         Rectangle2D band;
 864:         if (plot.getOrientation() == PlotOrientation.VERTICAL) {
 865:             band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(y1, y2),
 866:                 dataArea.getWidth(), Math.abs(y2 - y1));
 867:         }
 868:         else {
 869:             band = new Rectangle2D.Double(Math.min(y1, y2), dataArea.getMinY(),
 870:                     Math.abs(y2 - y1), dataArea.getHeight());
 871:         }
 872:         Paint paint = plot.getRangeTickBandPaint();
 873: 
 874:         if (paint != null) {
 875:             g2.setPaint(paint);
 876:             g2.fill(band);
 877:         }
 878: 
 879:     }
 880: 
 881:     /**
 882:      * Draws a grid line against the range axis.
 883:      *
 884:      * @param g2  the graphics device.
 885:      * @param plot  the plot.
 886:      * @param axis  the value axis.
 887:      * @param dataArea  the area for plotting data (not yet adjusted for any
 888:      *                  3D effect).
 889:      * @param value  the value at which the grid line should be drawn.
 890:      */
 891:     public void drawDomainGridLine(Graphics2D g2,
 892:                                    XYPlot plot,
 893:                                    ValueAxis axis,
 894:                                    Rectangle2D dataArea,
 895:                                    double value) {
 896: 
 897:         Range range = axis.getRange();
 898:         if (!range.contains(value)) {
 899:             return;
 900:         }
 901: 
 902:         PlotOrientation orientation = plot.getOrientation();
 903:         double v = axis.valueToJava2D(value, dataArea,
 904:                 plot.getDomainAxisEdge());
 905:         Line2D line = null;
 906:         if (orientation == PlotOrientation.HORIZONTAL) {
 907:             line = new Line2D.Double(dataArea.getMinX(), v,
 908:                     dataArea.getMaxX(), v);
 909:         }
 910:         else if (orientation == PlotOrientation.VERTICAL) {
 911:             line = new Line2D.Double(v, dataArea.getMinY(), v,
 912:                     dataArea.getMaxY());
 913:         }
 914: 
 915:         Paint paint = plot.getDomainGridlinePaint();
 916:         Stroke stroke = plot.getDomainGridlineStroke();
 917:         g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
 918:         g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
 919:         g2.draw(line);
 920: 
 921:     }
 922: 
 923:     /**
 924:      * Draws a line perpendicular to the domain axis.
 925:      *
 926:      * @param g2  the graphics device.
 927:      * @param plot  the plot.
 928:      * @param axis  the value axis.
 929:      * @param dataArea  the area for plotting data (not yet adjusted for any 3D
 930:      *                  effect).
 931:      * @param value  the value at which the grid line should be drawn.
 932:      * @param paint  the paint.
 933:      * @param stroke  the stroke.
 934:      *
 935:      * @since 1.0.5
 936:      */
 937:     public void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis,
 938:             Rectangle2D dataArea, double value, Paint paint, Stroke stroke) {
 939: 
 940:         Range range = axis.getRange();
 941:         if (!range.contains(value)) {
 942:             return;
 943:         }
 944: 
 945:         PlotOrientation orientation = plot.getOrientation();
 946:         Line2D line = null;
 947:         double v = axis.valueToJava2D(value, dataArea,
 948:                 plot.getDomainAxisEdge());
 949:         if (orientation == PlotOrientation.HORIZONTAL) {
 950:             line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(),
 951:                     v);
 952:         }
 953:         else if (orientation == PlotOrientation.VERTICAL) {
 954:             line = new Line2D.Double(v, dataArea.getMinY(), v,
 955:                     dataArea.getMaxY());
 956:         }
 957: 
 958:         g2.setPaint(paint);
 959:         g2.setStroke(stroke);
 960:         g2.draw(line);
 961: 
 962:     }
 963: 
 964:     /**
 965:      * Draws a line perpendicular to the range axis.
 966:      *
 967:      * @param g2  the graphics device.
 968:      * @param plot  the plot.
 969:      * @param axis  the value axis.
 970:      * @param dataArea  the area for plotting data (not yet adjusted for any 3D
 971:      *                  effect).
 972:      * @param value  the value at which the grid line should be drawn.
 973:      * @param paint  the paint.
 974:      * @param stroke  the stroke.
 975:      */
 976:     public void drawRangeLine(Graphics2D g2,
 977:                               XYPlot plot,
 978:                               ValueAxis axis,
 979:                               Rectangle2D dataArea,
 980:                               double value,
 981:                               Paint paint,
 982:                               Stroke stroke) {
 983: 
 984:         Range range = axis.getRange();
 985:         if (!range.contains(value)) {
 986:             return;
 987:         }
 988: 
 989:         PlotOrientation orientation = plot.getOrientation();
 990:         Line2D line = null;
 991:         double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge());
 992:         if (orientation == PlotOrientation.HORIZONTAL) {
 993:             line = new Line2D.Double(v, dataArea.getMinY(), v,
 994:                     dataArea.getMaxY());
 995:         }
 996:         else if (orientation == PlotOrientation.VERTICAL) {
 997:             line = new Line2D.Double(dataArea.getMinX(), v,
 998:                     dataArea.getMaxX(), v);
 999:         }
1000: 
1001:         g2.setPaint(paint);
1002:         g2.setStroke(stroke);
1003:         g2.draw(line);
1004: 
1005:     }
1006: 
1007:     /**
1008:      * Draws a vertical line on the chart to represent a 'range marker'.
1009:      *
1010:      * @param g2  the graphics device.
1011:      * @param plot  the plot.
1012:      * @param domainAxis  the domain axis.
1013:      * @param marker  the marker line.
1014:      * @param dataArea  the axis data area.
1015:      */
1016:     public void drawDomainMarker(Graphics2D g2,
1017:                                  XYPlot plot,
1018:                                  ValueAxis domainAxis,
1019:                                  Marker marker,
1020:                                  Rectangle2D dataArea) {
1021: 
1022:         if (marker instanceof ValueMarker) {
1023:             ValueMarker vm = (ValueMarker) marker;
1024:             double value = vm.getValue();
1025:             Range range = domainAxis.getRange();
1026:             if (!range.contains(value)) {
1027:                 return;
1028:             }
1029: 
1030:             double v = domainAxis.valueToJava2D(value, dataArea,
1031:                     plot.getDomainAxisEdge());
1032: 
1033:             PlotOrientation orientation = plot.getOrientation();
1034:             Line2D line = null;
1035:             if (orientation == PlotOrientation.HORIZONTAL) {
1036:                 line = new Line2D.Double(dataArea.getMinX(), v,
1037:                         dataArea.getMaxX(), v);
1038:             }
1039:             else if (orientation == PlotOrientation.VERTICAL) {
1040:                 line = new Line2D.Double(v, dataArea.getMinY(), v,
1041:                         dataArea.getMaxY());
1042:             }
1043: 
1044:             final Composite originalComposite = g2.getComposite();
1045:             g2.setComposite(AlphaComposite.getInstance(
1046:                     AlphaComposite.SRC_OVER, marker.getAlpha()));
1047:             g2.setPaint(marker.getPaint());
1048:             g2.setStroke(marker.getStroke());
1049:             g2.draw(line);
1050: 
1051:             String label = marker.getLabel();
1052:             RectangleAnchor anchor = marker.getLabelAnchor();
1053:             if (label != null) {
1054:                 Font labelFont = marker.getLabelFont();
1055:                 g2.setFont(labelFont);
1056:                 g2.setPaint(marker.getLabelPaint());
1057:                 Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1058:                         g2, orientation, dataArea, line.getBounds2D(),
1059:                         marker.getLabelOffset(),
1060:                         LengthAdjustmentType.EXPAND, anchor);
1061:                 TextUtilities.drawAlignedString(label, g2,
1062:                         (float) coordinates.getX(), (float) coordinates.getY(),
1063:                         marker.getLabelTextAnchor());
1064:             }
1065:             g2.setComposite(originalComposite);
1066:         }
1067:         else if (marker instanceof IntervalMarker) {
1068:             IntervalMarker im = (IntervalMarker) marker;
1069:             double start = im.getStartValue();
1070:             double end = im.getEndValue();
1071:             Range range = domainAxis.getRange();
1072:             if (!(range.intersects(start, end))) {
1073:                 return;
1074:             }
1075: 
1076:             double start2d = domainAxis.valueToJava2D(start, dataArea,
1077:                     plot.getDomainAxisEdge());
1078:             double end2d = domainAxis.valueToJava2D(end, dataArea,
1079:                     plot.getDomainAxisEdge());
1080:             double low = Math.min(start2d, end2d);
1081:             double high = Math.max(start2d, end2d);
1082: 
1083:             PlotOrientation orientation = plot.getOrientation();
1084:             Rectangle2D rect = null;
1085:             if (orientation == PlotOrientation.HORIZONTAL) {
1086:                 // clip top and bottom bounds to data area
1087:                 low = Math.max(low, dataArea.getMinY());
1088:                 high = Math.min(high, dataArea.getMaxY());
1089:                 rect = new Rectangle2D.Double(dataArea.getMinX(),
1090:                         low, dataArea.getWidth(),
1091:                         high - low);
1092:             }
1093:             else if (orientation == PlotOrientation.VERTICAL) {
1094:                 // clip left and right bounds to data area
1095:                 low = Math.max(low, dataArea.getMinX());
1096:                 high = Math.min(high, dataArea.getMaxX());
1097:                 rect = new Rectangle2D.Double(low,
1098:                         dataArea.getMinY(), high - low,
1099:                         dataArea.getHeight());
1100:             }
1101: 
1102:             final Composite originalComposite = g2.getComposite();
1103:             g2.setComposite(AlphaComposite.getInstance(
1104:                     AlphaComposite.SRC_OVER, marker.getAlpha()));
1105:             Paint p = marker.getPaint();
1106:             if (p instanceof GradientPaint) {
1107:                 GradientPaint gp = (GradientPaint) p;
1108:                 GradientPaintTransformer t = im.getGradientPaintTransformer();
1109:                 if (t != null) {
1110:                     gp = t.transform(gp, rect);
1111:                 }
1112:                 g2.setPaint(gp);
1113:             }
1114:             else {
1115:                 g2.setPaint(p);
1116:             }
1117:             g2.fill(rect);
1118: 
1119:             // now draw the outlines, if visible...
1120:             if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
1121:                 if (orientation == PlotOrientation.VERTICAL) {
1122:                     Line2D line = new Line2D.Double();
1123:                     double y0 = dataArea.getMinY();
1124:                     double y1 = dataArea.getMaxY();
1125:                     g2.setPaint(im.getOutlinePaint());
1126:                     g2.setStroke(im.getOutlineStroke());
1127:                     if (range.contains(start)) {
1128:                         line.setLine(start2d, y0, start2d, y1);
1129:                         g2.draw(line);
1130:                     }
1131:                     if (range.contains(end)) {
1132:                         line.setLine(end2d, y0, end2d, y1);
1133:                         g2.draw(line);
1134:                     }
1135:                 }
1136:                 else { // PlotOrientation.HORIZONTAL
1137:                     Line2D line = new Line2D.Double();
1138:                     double x0 = dataArea.getMinX();
1139:                     double x1 = dataArea.getMaxX();
1140:                     g2.setPaint(im.getOutlinePaint());
1141:                     g2.setStroke(im.getOutlineStroke());
1142:                     if (range.contains(start)) {
1143:                         line.setLine(x0, start2d, x1, start2d);
1144:                         g2.draw(line);
1145:                     }
1146:                     if (range.contains(end)) {
1147:                         line.setLine(x0, end2d, x1, end2d);
1148:                         g2.draw(line);
1149:                     }
1150:                 }
1151:             }
1152: 
1153:             String label = marker.getLabel();
1154:             RectangleAnchor anchor = marker.getLabelAnchor();
1155:             if (label != null) {
1156:                 Font labelFont = marker.getLabelFont();
1157:                 g2.setFont(labelFont);
1158:                 g2.setPaint(marker.getLabelPaint());
1159:                 Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1160:                         g2, orientation, dataArea, rect,
1161:                         marker.getLabelOffset(), marker.getLabelOffsetType(),
1162:                         anchor);
1163:                 TextUtilities.drawAlignedString(label, g2,
1164:                         (float) coordinates.getX(), (float) coordinates.getY(),
1165:                         marker.getLabelTextAnchor());
1166:             }
1167:             g2.setComposite(originalComposite);
1168: 
1169:         }
1170: 
1171:     }
1172: 
1173:     /**
1174:      * Calculates the (x, y) coordinates for drawing a marker label.
1175:      *
1176:      * @param g2  the graphics device.
1177:      * @param orientation  the plot orientation.
1178:      * @param dataArea  the data area.
1179:      * @param markerArea  the rectangle surrounding the marker area.
1180:      * @param markerOffset  the marker label offset.
1181:      * @param labelOffsetType  the label offset type.
1182:      * @param anchor  the label anchor.
1183:      *
1184:      * @return The coordinates for drawing the marker label.
1185:      */
1186:     protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
1187:             PlotOrientation orientation,
1188:             Rectangle2D dataArea,
1189:             Rectangle2D markerArea,
1190:             RectangleInsets markerOffset,
1191:             LengthAdjustmentType labelOffsetType,
1192:             RectangleAnchor anchor) {
1193: 
1194:         Rectangle2D anchorRect = null;
1195:         if (orientation == PlotOrientation.HORIZONTAL) {
1196:             anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1197:                     LengthAdjustmentType.CONTRACT, labelOffsetType);
1198:         }
1199:         else if (orientation == PlotOrientation.VERTICAL) {
1200:             anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1201:                     labelOffsetType, LengthAdjustmentType.CONTRACT);
1202:         }
1203:         return RectangleAnchor.coordinates(anchorRect, anchor);
1204: 
1205:     }
1206: 
1207:     /**
1208:      * Draws a horizontal line across the chart to represent a 'range marker'.
1209:      *
1210:      * @param g2  the graphics device.
1211:      * @param plot  the plot.
1212:      * @param rangeAxis  the range axis.
1213:      * @param marker  the marker line.
1214:      * @param dataArea  the axis data area.
1215:      */
1216:     public void drawRangeMarker(Graphics2D g2,
1217:                                 XYPlot plot,
1218:                                 ValueAxis rangeAxis,
1219:                                 Marker marker,
1220:                                 Rectangle2D dataArea) {
1221: 
1222:         if (marker instanceof ValueMarker) {
1223:             ValueMarker vm = (ValueMarker) marker;
1224:             double value = vm.getValue();
1225:             Range range = rangeAxis.getRange();
1226:             if (!range.contains(value)) {
1227:                 return;
1228:             }
1229: 
1230:             double v = rangeAxis.valueToJava2D(value, dataArea,
1231:                     plot.getRangeAxisEdge());
1232:             PlotOrientation orientation = plot.getOrientation();
1233:             Line2D line = null;
1234:             if (orientation == PlotOrientation.HORIZONTAL) {
1235:                 line = new Line2D.Double(v, dataArea.getMinY(), v,
1236:                         dataArea.getMaxY());
1237:             }
1238:             else if (orientation == PlotOrientation.VERTICAL) {
1239:                 line = new Line2D.Double(dataArea.getMinX(), v,
1240:                         dataArea.getMaxX(), v);
1241:             }
1242: 
1243:             final Composite originalComposite = g2.getComposite();
1244:             g2.setComposite(AlphaComposite.getInstance(
1245:                     AlphaComposite.SRC_OVER, marker.getAlpha()));
1246:             g2.setPaint(marker.getPaint());
1247:             g2.setStroke(marker.getStroke());
1248:             g2.draw(line);
1249: 
1250:             String label = marker.getLabel();
1251:             RectangleAnchor anchor = marker.getLabelAnchor();
1252:             if (label != null) {
1253:                 Font labelFont = marker.getLabelFont();
1254:                 g2.setFont(labelFont);
1255:                 g2.setPaint(marker.getLabelPaint());
1256:                 Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1257:                         g2, orientation, dataArea, line.getBounds2D(),
1258:                         marker.getLabelOffset(),
1259:                         LengthAdjustmentType.EXPAND, anchor);
1260:                 TextUtilities.drawAlignedString(label, g2,
1261:                         (float) coordinates.getX(), (float) coordinates.getY(),
1262:                         marker.getLabelTextAnchor());
1263:             }
1264:             g2.setComposite(originalComposite);
1265:         }
1266:         else if (marker instanceof IntervalMarker) {
1267:             IntervalMarker im = (IntervalMarker) marker;
1268:             double start = im.getStartValue();
1269:             double end = im.getEndValue();
1270:             Range range = rangeAxis.getRange();
1271:             if (!(range.intersects(start, end))) {
1272:                 return;
1273:             }
1274: 
1275:             double start2d = rangeAxis.valueToJava2D(start, dataArea,
1276:                     plot.getRangeAxisEdge());
1277:             double end2d = rangeAxis.valueToJava2D(end, dataArea,
1278:                     plot.getRangeAxisEdge());
1279:             double low = Math.min(start2d, end2d);
1280:             double high = Math.max(start2d, end2d);
1281: 
1282:             PlotOrientation orientation = plot.getOrientation();
1283:             Rectangle2D rect = null;
1284:             if (orientation == PlotOrientation.HORIZONTAL) {
1285:                 // clip left and right bounds to data area
1286:                 low = Math.max(low, dataArea.getMinX());
1287:                 high = Math.min(high, dataArea.getMaxX());
1288:                 rect = new Rectangle2D.Double(low,
1289:                         dataArea.getMinY(), high - low,
1290:                         dataArea.getHeight());
1291:             }
1292:             else if (orientation == PlotOrientation.VERTICAL) {
1293:                 // clip top and bottom bounds to data area
1294:                 low = Math.max(low, dataArea.getMinY());
1295:                 high = Math.min(high, dataArea.getMaxY());
1296:                 rect = new Rectangle2D.Double(dataArea.getMinX(),
1297:                         low, dataArea.getWidth(),
1298:                         high - low);
1299:             }
1300: 
1301:             final Composite originalComposite = g2.getComposite();
1302:             g2.setComposite(AlphaComposite.getInstance(
1303:                     AlphaComposite.SRC_OVER, marker.getAlpha()));
1304:             Paint p = marker.getPaint();
1305:             if (p instanceof GradientPaint) {
1306:                 GradientPaint gp = (GradientPaint) p;
1307:                 GradientPaintTransformer t = im.getGradientPaintTransformer();
1308:                 if (t != null) {
1309:                     gp = t.transform(gp, rect);
1310:                 }
1311:                 g2.setPaint(gp);
1312:             }
1313:             else {
1314:                 g2.setPaint(p);
1315:             }
1316:             g2.fill(rect);
1317: 
1318:             // now draw the outlines, if visible...
1319:             if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
1320:                 if (orientation == PlotOrientation.VERTICAL) {
1321:                     Line2D line = new Line2D.Double();
1322:                     double x0 = dataArea.getMinX();
1323:                     double x1 = dataArea.getMaxX();
1324:                     g2.setPaint(im.getOutlinePaint());
1325:                     g2.setStroke(im.getOutlineStroke());
1326:                     if (range.contains(start)) {
1327:                         line.setLine(x0, start2d, x1, start2d);
1328:                         g2.draw(line);
1329:                     }
1330:                     if (range.contains(end)) {
1331:                         line.setLine(x0, end2d, x1, end2d);
1332:                         g2.draw(line);
1333:                     }
1334:                 }
1335:                 else { // PlotOrientation.HORIZONTAL
1336:                     Line2D line = new Line2D.Double();
1337:                     double y0 = dataArea.getMinY();
1338:                     double y1 = dataArea.getMaxY();
1339:                     g2.setPaint(im.getOutlinePaint());
1340:                     g2.setStroke(im.getOutlineStroke());
1341:                     if (range.contains(start)) {
1342:                         line.setLine(start2d, y0, start2d, y1);
1343:                         g2.draw(line);
1344:                     }
1345:                     if (range.contains(end)) {
1346:                         line.setLine(end2d, y0, end2d, y1);
1347:                         g2.draw(line);
1348:                     }
1349:                 }
1350:             }
1351: 
1352:             String label = marker.getLabel();
1353:             RectangleAnchor anchor = marker.getLabelAnchor();
1354:             if (label != null) {
1355:                 Font labelFont = marker.getLabelFont();
1356:                 g2.setFont(labelFont);
1357:                 g2.setPaint(marker.getLabelPaint());
1358:                 Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1359:                         g2, orientation, dataArea, rect,
1360:                         marker.getLabelOffset(), marker.getLabelOffsetType(),
1361:                         anchor);
1362:                 TextUtilities.drawAlignedString(label, g2,
1363:                         (float) coordinates.getX(), (float) coordinates.getY(),
1364:                         marker.getLabelTextAnchor());
1365:             }
1366:             g2.setComposite(originalComposite);
1367:         }
1368:     }
1369: 
1370:     /**
1371:      * Calculates the (x, y) coordinates for drawing a marker label.
1372:      *
1373:      * @param g2  the graphics device.
1374:      * @param orientation  the plot orientation.
1375:      * @param dataArea  the data area.
1376:      * @param markerArea  the marker area.
1377:      * @param markerOffset  the marker offset.
1378:      * @param labelOffsetForRange  ??
1379:      * @param anchor  the label anchor.
1380:      *
1381:      * @return The coordinates for drawing the marker label.
1382:      */
1383:     private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2,
1384:                                       PlotOrientation orientation,
1385:                                       Rectangle2D dataArea,
1386:                                       Rectangle2D markerArea,
1387:                                       RectangleInsets markerOffset,
1388:                                       LengthAdjustmentType labelOffsetForRange,
1389:                                       RectangleAnchor anchor) {
1390: 
1391:         Rectangle2D anchorRect = null;
1392:         if (orientation == PlotOrientation.HORIZONTAL) {
1393:             anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1394:                     labelOffsetForRange, LengthAdjustmentType.CONTRACT);
1395:         }
1396:         else if (orientation == PlotOrientation.VERTICAL) {
1397:             anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1398:                     LengthAdjustmentType.CONTRACT, labelOffsetForRange);
1399:         }
1400:         return RectangleAnchor.coordinates(anchorRect, anchor);
1401: 
1402:     }
1403: 
1404:     /**
1405:      * Returns a clone of the renderer.
1406:      *
1407:      * @return A clone.
1408:      *
1409:      * @throws CloneNotSupportedException if the renderer does not support
1410:      *         cloning.
1411:      */
1412:     protected Object clone() throws CloneNotSupportedException {
1413:         AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone();
1414:         // 'plot' : just retain reference, not a deep copy
1415: 
1416:         if (this.itemLabelGenerator != null
1417:                 && this.itemLabelGenerator instanceof PublicCloneable) {
1418:             PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator;
1419:             clone.itemLabelGenerator = (XYItemLabelGenerator) pc.clone();
1420:         }
1421:         clone.itemLabelGeneratorList
1422:                 = (ObjectList) this.itemLabelGeneratorList.clone();
1423:         if (this.baseItemLabelGenerator != null
1424:                 && this.baseItemLabelGenerator instanceof PublicCloneable) {
1425:             PublicCloneable pc = (PublicCloneable) this.baseItemLabelGenerator;
1426:             clone.baseItemLabelGenerator = (XYItemLabelGenerator) pc.clone();
1427:         }
1428: 
1429:         if (this.toolTipGenerator != null
1430:                 && this.toolTipGenerator instanceof PublicCloneable) {
1431:             PublicCloneable pc = (PublicCloneable) this.toolTipGenerator;
1432:             clone.toolTipGenerator = (XYToolTipGenerator) pc.clone();
1433:         }
1434:         clone.toolTipGeneratorList
1435:                 = (ObjectList) this.toolTipGeneratorList.clone();
1436:         if (this.baseToolTipGenerator != null
1437:                 && this.baseToolTipGenerator instanceof PublicCloneable) {
1438:             PublicCloneable pc = (PublicCloneable) this.baseToolTipGenerator;
1439:             clone.baseToolTipGenerator = (XYToolTipGenerator) pc.clone();
1440:         }
1441: 
1442:         if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
1443:             clone.legendItemLabelGenerator = (XYSeriesLabelGenerator)
1444:                     ObjectUtilities.clone(this.legendItemLabelGenerator);
1445:         }
1446:         if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
1447:             clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator)
1448:                     ObjectUtilities.clone(this.legendItemToolTipGenerator);
1449:         }
1450:         if (clone.legendItemURLGenerator instanceof PublicCloneable) {
1451:             clone.legendItemURLGenerator = (XYSeriesLabelGenerator)
1452:                     ObjectUtilities.clone(this.legendItemURLGenerator);
1453:         }
1454: 
1455:         clone.foregroundAnnotations = (List) ObjectUtilities.deepClone(
1456:                 this.foregroundAnnotations);
1457:         clone.backgroundAnnotations = (List) ObjectUtilities.deepClone(
1458:                 this.backgroundAnnotations);
1459: 
1460:         if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
1461:             clone.legendItemLabelGenerator = (XYSeriesLabelGenerator)
1462:                     ObjectUtilities.clone(this.legendItemLabelGenerator);
1463:         }
1464:         if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
1465:             clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator)
1466:                     ObjectUtilities.clone(this.legendItemToolTipGenerator);
1467:         }
1468:         if (clone.legendItemURLGenerator instanceof PublicCloneable) {
1469:             clone.legendItemURLGenerator = (XYSeriesLabelGenerator)
1470:                     ObjectUtilities.clone(this.legendItemURLGenerator);
1471:         }
1472: 
1473:         return clone;
1474:     }
1475: 
1476:     /**
1477:      * Tests this renderer for equality with another object.
1478:      *
1479:      * @param obj  the object (<code>null</code> permitted).
1480:      *
1481:      * @return <code>true</code> or <code>false</code>.
1482:      */
1483:     public boolean equals(Object obj) {
1484:         if (obj == this) {
1485:             return true;
1486:         }
1487:         if (!(obj instanceof AbstractXYItemRenderer)) {
1488:             return false;
1489:         }
1490:         AbstractXYItemRenderer that = (AbstractXYItemRenderer) obj;
1491:         if (!ObjectUtilities.equal(this.itemLabelGenerator,
1492:                 that.itemLabelGenerator)) {
1493:             return false;
1494:         }
1495:         if (!this.itemLabelGeneratorList.equals(that.itemLabelGeneratorList)) {
1496:             return false;
1497:         }
1498:         if (!ObjectUtilities.equal(this.baseItemLabelGenerator,
1499:                 that.baseItemLabelGenerator)) {
1500:             return false;
1501:         }
1502:         if (!ObjectUtilities.equal(this.toolTipGenerator,
1503:                 that.toolTipGenerator)) {
1504:             return false;
1505:         }
1506:         if (!this.toolTipGeneratorList.equals(that.toolTipGeneratorList)) {
1507:             return false;
1508:         }
1509:         if (!ObjectUtilities.equal(this.baseToolTipGenerator,
1510:                 that.baseToolTipGenerator)) {
1511:             return false;
1512:         }
1513:         if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) {
1514:             return false;
1515:         }
1516:         if (!this.foregroundAnnotations.equals(that.foregroundAnnotations)) {
1517:             return false;
1518:         }
1519:         if (!this.backgroundAnnotations.equals(that.backgroundAnnotations)) {
1520:             return false;
1521:         }
1522:         if (this.defaultEntityRadius != that.defaultEntityRadius) {
1523:             return false;
1524:         }
1525:         if (!ObjectUtilities.equal(this.legendItemLabelGenerator,
1526:                 that.legendItemLabelGenerator)) {
1527:             return false;
1528:         }
1529:         if (!ObjectUtilities.equal(this.legendItemToolTipGenerator,
1530:                 that.legendItemToolTipGenerator)) {
1531:             return false;
1532:         }
1533:         if (!ObjectUtilities.equal(this.legendItemURLGenerator,
1534:                 that.legendItemURLGenerator)) {
1535:             return false;
1536:         }
1537:         return super.equals(obj);
1538:     }
1539: 
1540:     /**
1541:      * Returns the drawing supplier from the plot.
1542:      *
1543:      * @return The drawing supplier (possibly <code>null</code>).
1544:      */
1545:     public DrawingSupplier getDrawingSupplier() {
1546:         DrawingSupplier result = null;
1547:         XYPlot p = getPlot();
1548:         if (p != null) {
1549:             result = p.getDrawingSupplier();
1550:         }
1551:         return result;
1552:     }
1553: 
1554:     /**
1555:      * Considers the current (x, y) coordinate and updates the crosshair point
1556:      * if it meets the criteria (usually means the (x, y) coordinate is the
1557:      * closest to the anchor point so far).
1558:      *
1559:      * @param crosshairState  the crosshair state (<code>null</code> permitted,
1560:      *                        but the method does nothing in that case).
1561:      * @param x  the x-value (in data space).
1562:      * @param y  the y-value (in data space).
1563:      * @param transX  the x-value translated to Java2D space.
1564:      * @param transY  the y-value translated to Java2D space.
1565:      * @param orientation  the plot orientation (<code>null</code> not
1566:      *                     permitted).
1567:      *
1568:      * @deprecated Use {@link #updateCrosshairValues(CrosshairState, double,
1569:      *         double, int, int, double, double, PlotOrientation)} -- see bug
1570:      *         report 1086307.
1571:      */
1572:     protected void updateCrosshairValues(CrosshairState crosshairState,
1573:             double x, double y, double transX, double transY,
1574:             PlotOrientation orientation) {
1575:         updateCrosshairValues(crosshairState, x, y, 0, 0, transX, transY,
1576:                 orientation);
1577:     }
1578: 
1579:     /**
1580:      * Considers the current (x, y) coordinate and updates the crosshair point
1581:      * if it meets the criteria (usually means the (x, y) coordinate is the
1582:      * closest to the anchor point so far).
1583:      *
1584:      * @param crosshairState  the crosshair state (<code>null</code> permitted,
1585:      *                        but the method does nothing in that case).
1586:      * @param x  the x-value (in data space).
1587:      * @param y  the y-value (in data space).
1588:      * @param domainAxisIndex  the index of the domain axis for the point.
1589:      * @param rangeAxisIndex  the index of the range axis for the point.
1590:      * @param transX  the x-value translated to Java2D space.
1591:      * @param transY  the y-value translated to Java2D space.
1592:      * @param orientation  the plot orientation (<code>null</code> not
1593:      *                     permitted).
1594:      *
1595:      * @since 1.0.4
1596:      */
1597:     protected void updateCrosshairValues(CrosshairState crosshairState,
1598:             double x, double y, int domainAxisIndex, int rangeAxisIndex,
1599:             double transX, double transY, PlotOrientation orientation) {
1600: 
1601:         if (orientation == null) {
1602:             throw new IllegalArgumentException("Null 'orientation' argument.");
1603:         }
1604: 
1605:         if (crosshairState != null) {
1606:             // do we need to update the crosshair values?
1607:             if (this.plot.isDomainCrosshairLockedOnData()) {
1608:                 if (this.plot.isRangeCrosshairLockedOnData()) {
1609:                     // both axes
1610:                     crosshairState.updateCrosshairPoint(x, y, domainAxisIndex,
1611:                             rangeAxisIndex, transX, transY, orientation);
1612:                 }
1613:                 else {
1614:                     // just the domain axis...
1615:                     crosshairState.updateCrosshairX(x, domainAxisIndex);
1616:                 }
1617:             }
1618:             else {
1619:                 if (this.plot.isRangeCrosshairLockedOnData()) {
1620:                     // just the range axis...
1621:                     crosshairState.updateCrosshairY(y, rangeAxisIndex);
1622:                 }
1623:             }
1624:         }
1625: 
1626:     }
1627: 
1628:     /**
1629:      * Draws an item label.
1630:      *
1631:      * @param g2  the graphics device.
1632:      * @param orientation  the orientation.
1633:      * @param dataset  the dataset.
1634:      * @param series  the series index (zero-based).
1635:      * @param item  the item index (zero-based).
1636:      * @param x  the x coordinate (in Java2D space).
1637:      * @param y  the y coordinate (in Java2D space).
1638:      * @param negative  indicates a negative value (which affects the item
1639:      *                  label position).
1640:      */
1641:     protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation,
1642:             XYDataset dataset, int series, int item, double x, double y,
1643:             boolean negative) {
1644: 
1645:         XYItemLabelGenerator generator = getItemLabelGenerator(series, item);
1646:         if (generator != null) {
1647:             Font labelFont = getItemLabelFont(series, item);
1648:             Paint paint = getItemLabelPaint(series, item);
1649:             g2.setFont(labelFont);
1650:             g2.setPaint(paint);
1651:             String label = generator.generateLabel(dataset, series, item);
1652: 
1653:             // get the label position..
1654:             ItemLabelPosition position = null;
1655:             if (!negative) {
1656:                 position = getPositiveItemLabelPosition(series, item);
1657:             }
1658:             else {
1659:                 position = getNegativeItemLabelPosition(series, item);
1660:             }
1661: 
1662:             // work out the label anchor point...
1663:             Point2D anchorPoint = calculateLabelAnchorPoint(
1664:                     position.getItemLabelAnchor(), x, y, orientation);
1665:             TextUtilities.drawRotatedString(label, g2,
1666:                     (float) anchorPoint.getX(), (float) anchorPoint.getY(),
1667:                     position.getTextAnchor(), position.getAngle(),
1668:                     position.getRotationAnchor());
1669:         }
1670: 
1671:     }
1672: 
1673:     /**
1674:      * Draws all the annotations for the specified layer.
1675:      *
1676:      * @param g2  the graphics device.
1677:      * @param dataArea  the data area.
1678:      * @param domainAxis  the domain axis.
1679:      * @param rangeAxis  the range axis.
1680:      * @param layer  the layer.
1681:      * @param info  the plot rendering info.
1682:      */
1683:     public void drawAnnotations(Graphics2D g2,
1684:                                 Rectangle2D dataArea,
1685:                                 ValueAxis domainAxis,
1686:                                 ValueAxis rangeAxis,
1687:                                 Layer layer,
1688:                                 PlotRenderingInfo info) {
1689: 
1690:         Iterator iterator = null;
1691:         if (layer.equals(Layer.FOREGROUND)) {
1692:             iterator = this.foregroundAnnotations.iterator();
1693:         }
1694:         else if (layer.equals(Layer.BACKGROUND)) {
1695:             iterator = this.backgroundAnnotations.iterator();
1696:         }
1697:         else {
1698:             // should not get here
1699:             throw new RuntimeException("Unknown layer.");
1700:         }
1701:         while (iterator.hasNext()) {
1702:             XYAnnotation annotation = (XYAnnotation) iterator.next();
1703:             annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis,
1704:                     0, info);
1705:         }
1706: 
1707:     }
1708: 
1709:     /**
1710:      * Adds an entity to the collection.
1711:      *
1712:      * @param entities  the entity collection being populated.
1713:      * @param area  the entity area (if <code>null</code> a default will be
1714:      *              used).
1715:      * @param dataset  the dataset.
1716:      * @param series  the series.
1717:      * @param item  the item.
1718:      * @param entityX  the entity's center x-coordinate in user space (only
1719:      *                 used if <code>area</code> is <code>null</code>).
1720:      * @param entityY  the entity's center y-coordinate in user space (only
1721:      *                 used if <code>area</code> is <code>null</code>).
1722:      */
1723:     protected void addEntity(EntityCollection entities, Shape area,
1724:                              XYDataset dataset, int series, int item,
1725:                              double entityX, double entityY) {
1726:         if (!getItemCreateEntity(series, item)) {
1727:             return;
1728:         }
1729:         Shape hotspot = area;
1730:         if (hotspot == null) {
1731:             double w = this.defaultEntityRadius * 2;
1732:             if (getPlot().getOrientation() == PlotOrientation.VERTICAL) {
1733:                 hotspot = new Ellipse2D.Double(
1734:                         entityX - this.defaultEntityRadius,
1735:                         entityY - this.defaultEntityRadius, w, w);
1736:             }
1737:             else {
1738:                 hotspot = new Ellipse2D.Double(
1739:                         entityY - this.defaultEntityRadius,
1740:                         entityX - this.defaultEntityRadius, w, w);
1741:             }
1742:         }
1743:         String tip = null;
1744:         XYToolTipGenerator generator = getToolTipGenerator(series, item);
1745:         if (generator != null) {
1746:             tip = generator.generateToolTip(dataset, series, item);
1747:         }
1748:         String url = null;
1749:         if (getURLGenerator() != null) {
1750:             url = getURLGenerator().generateURL(dataset, series, item);
1751:         }
1752:         XYItemEntity entity = new XYItemEntity(hotspot, dataset, series, item,
1753:                 tip, url);
1754:         entities.add(entity);
1755:     }
1756: 
1757:     /**
1758:      * Returns <code>true</code> if the specified point (x, y) falls within or
1759:      * on the boundary of the specified rectangle.
1760:      *
1761:      * @param rect  the rectangle (<code>null</code> not permitted).
1762:      * @param x  the x-coordinate.
1763:      * @param y  the y-coordinate.
1764:      *
1765:      * @return A boolean.
1766:      *
1767:      * @since 1.0.10
1768:      */
1769:     public static boolean isPointInRect(Rectangle2D rect, double x, double y) {
1770:         // TODO: For JFreeChart 1.2.0, this method should go in the
1771:         //       ShapeUtilities class
1772:         return (x >= rect.getMinX() && x <= rect.getMaxX()
1773:                 && y >= rect.getMinY() && y <= rect.getMaxY());
1774:     }
1775: 
1776: }