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