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