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