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