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