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 099package org.jfree.chart.renderer.category; 100 101import java.awt.AlphaComposite; 102import java.awt.Composite; 103import java.awt.Font; 104import java.awt.GradientPaint; 105import java.awt.Graphics2D; 106import java.awt.Paint; 107import java.awt.Shape; 108import java.awt.Stroke; 109import java.awt.geom.Line2D; 110import java.awt.geom.Point2D; 111import java.awt.geom.Rectangle2D; 112import java.io.Serializable; 113 114import org.jfree.chart.LegendItem; 115import org.jfree.chart.LegendItemCollection; 116import org.jfree.chart.axis.CategoryAxis; 117import org.jfree.chart.axis.ValueAxis; 118import org.jfree.chart.entity.CategoryItemEntity; 119import org.jfree.chart.entity.EntityCollection; 120import org.jfree.chart.event.RendererChangeEvent; 121import org.jfree.chart.labels.CategoryItemLabelGenerator; 122import org.jfree.chart.labels.CategorySeriesLabelGenerator; 123import org.jfree.chart.labels.CategoryToolTipGenerator; 124import org.jfree.chart.labels.ItemLabelPosition; 125import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator; 126import org.jfree.chart.plot.CategoryMarker; 127import org.jfree.chart.plot.CategoryPlot; 128import org.jfree.chart.plot.DrawingSupplier; 129import org.jfree.chart.plot.IntervalMarker; 130import org.jfree.chart.plot.Marker; 131import org.jfree.chart.plot.PlotOrientation; 132import org.jfree.chart.plot.PlotRenderingInfo; 133import org.jfree.chart.plot.ValueMarker; 134import org.jfree.chart.renderer.AbstractRenderer; 135import org.jfree.chart.urls.CategoryURLGenerator; 136import org.jfree.data.Range; 137import org.jfree.data.category.CategoryDataset; 138import org.jfree.data.general.DatasetUtilities; 139import org.jfree.text.TextUtilities; 140import org.jfree.ui.GradientPaintTransformer; 141import org.jfree.ui.LengthAdjustmentType; 142import org.jfree.ui.RectangleAnchor; 143import org.jfree.ui.RectangleInsets; 144import org.jfree.util.ObjectList; 145import org.jfree.util.ObjectUtilities; 146import 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 */ 154public 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 notifyListeners(new RendererChangeEvent(this)); 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 notifyListeners(new RendererChangeEvent(this)); 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(CategoryItemLabelGenerator generator) 370 { 371 this.baseItemLabelGenerator = generator; 372 notifyListeners(new RendererChangeEvent(this)); 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 notifyListeners(new RendererChangeEvent(this)); 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 org.jfree.chart.event.RendererChangeEvent} to all registered 456 * 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 notifyListeners(new RendererChangeEvent(this)); 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 notifyListeners(new RendererChangeEvent(this)); 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. 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 notifyListeners(new RendererChangeEvent(this)); 548 } 549 550 /** 551 * Sets the URL generator for a series. 552 * 553 * @param series the series index (zero based). 554 * @param generator the generator. 555 * 556 * @see #getSeriesItemURLGenerator(int) 557 */ 558 public void setSeriesItemURLGenerator(int series, 559 CategoryURLGenerator generator) { 560 this.itemURLGeneratorList.set(series, generator); 561 notifyListeners(new RendererChangeEvent(this)); 562 } 563 564 /** 565 * Returns the base item URL generator. 566 * 567 * @return The item URL generator. 568 * 569 * @see #setBaseItemURLGenerator(CategoryURLGenerator) 570 */ 571 public CategoryURLGenerator getBaseItemURLGenerator() { 572 return this.baseItemURLGenerator; 573 } 574 575 /** 576 * Sets the base item URL generator. 577 * 578 * @param generator the item URL generator. 579 * 580 * @see #getBaseItemURLGenerator() 581 */ 582 public void setBaseItemURLGenerator(CategoryURLGenerator generator) { 583 this.baseItemURLGenerator = generator; 584 notifyListeners(new RendererChangeEvent(this)); 585 } 586 587 /** 588 * Returns the number of rows in the dataset. This value is updated in the 589 * {@link AbstractCategoryItemRenderer#initialise} method. 590 * 591 * @return The row count. 592 */ 593 public int getRowCount() { 594 return this.rowCount; 595 } 596 597 /** 598 * Returns the number of columns in the dataset. This value is updated in 599 * the {@link AbstractCategoryItemRenderer#initialise} method. 600 * 601 * @return The column count. 602 */ 603 public int getColumnCount() { 604 return this.columnCount; 605 } 606 607 /** 608 * Creates a new state instance---this method is called from the 609 * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int, 610 * PlotRenderingInfo)} method. Subclasses can override this method if 611 * they need to use a subclass of {@link CategoryItemRendererState}. 612 * 613 * @param info collects plot rendering info (<code>null</code> permitted). 614 * 615 * @return The new state instance (never <code>null</code>). 616 * 617 * @since 1.0.5 618 */ 619 protected CategoryItemRendererState createState(PlotRenderingInfo info) { 620 return new CategoryItemRendererState(info); 621 } 622 623 /** 624 * Initialises the renderer and returns a state object that will be used 625 * for the remainder of the drawing process for a single chart. The state 626 * object allows for the fact that the renderer may be used simultaneously 627 * by multiple threads (each thread will work with a separate state object). 628 * 629 * @param g2 the graphics device. 630 * @param dataArea the data area. 631 * @param plot the plot. 632 * @param rendererIndex the renderer index. 633 * @param info an object for returning information about the structure of 634 * the plot (<code>null</code> permitted). 635 * 636 * @return The renderer state. 637 */ 638 public CategoryItemRendererState initialise(Graphics2D g2, 639 Rectangle2D dataArea, 640 CategoryPlot plot, 641 int rendererIndex, 642 PlotRenderingInfo info) { 643 644 setPlot(plot); 645 CategoryDataset data = plot.getDataset(rendererIndex); 646 if (data != null) { 647 this.rowCount = data.getRowCount(); 648 this.columnCount = data.getColumnCount(); 649 } 650 else { 651 this.rowCount = 0; 652 this.columnCount = 0; 653 } 654 return createState(info); 655 656 } 657 658 /** 659 * Returns the range of values the renderer requires to display all the 660 * items from the specified dataset. 661 * 662 * @param dataset the dataset (<code>null</code> permitted). 663 * 664 * @return The range (or <code>null</code> if the dataset is 665 * <code>null</code> or empty). 666 */ 667 public Range findRangeBounds(CategoryDataset dataset) { 668 return DatasetUtilities.findRangeBounds(dataset); 669 } 670 671 /** 672 * Draws a background for the data area. The default implementation just 673 * gets the plot to draw the background, but some renderers will override 674 * this behaviour. 675 * 676 * @param g2 the graphics device. 677 * @param plot the plot. 678 * @param dataArea the data area. 679 */ 680 public void drawBackground(Graphics2D g2, 681 CategoryPlot plot, 682 Rectangle2D dataArea) { 683 684 plot.drawBackground(g2, dataArea); 685 686 } 687 688 /** 689 * Draws an outline for the data area. The default implementation just 690 * gets the plot to draw the outline, but some renderers will override this 691 * behaviour. 692 * 693 * @param g2 the graphics device. 694 * @param plot the plot. 695 * @param dataArea the data area. 696 */ 697 public void drawOutline(Graphics2D g2, 698 CategoryPlot plot, 699 Rectangle2D dataArea) { 700 701 plot.drawOutline(g2, dataArea); 702 703 } 704 705 /** 706 * Draws a grid line against the domain axis. 707 * <P> 708 * Note that this default implementation assumes that the horizontal axis 709 * is the domain axis. If this is not the case, you will need to override 710 * this method. 711 * 712 * @param g2 the graphics device. 713 * @param plot the plot. 714 * @param dataArea the area for plotting data (not yet adjusted for any 715 * 3D effect). 716 * @param value the Java2D value at which the grid line should be drawn. 717 * 718 * @see #drawRangeGridline(Graphics2D, CategoryPlot, ValueAxis, 719 * Rectangle2D, double) 720 */ 721 public void drawDomainGridline(Graphics2D g2, 722 CategoryPlot plot, 723 Rectangle2D dataArea, 724 double value) { 725 726 Line2D line = null; 727 PlotOrientation orientation = plot.getOrientation(); 728 729 if (orientation == PlotOrientation.HORIZONTAL) { 730 line = new Line2D.Double(dataArea.getMinX(), value, 731 dataArea.getMaxX(), value); 732 } 733 else if (orientation == PlotOrientation.VERTICAL) { 734 line = new Line2D.Double(value, dataArea.getMinY(), value, 735 dataArea.getMaxY()); 736 } 737 738 Paint paint = plot.getDomainGridlinePaint(); 739 if (paint == null) { 740 paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT; 741 } 742 g2.setPaint(paint); 743 744 Stroke stroke = plot.getDomainGridlineStroke(); 745 if (stroke == null) { 746 stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE; 747 } 748 g2.setStroke(stroke); 749 750 g2.draw(line); 751 752 } 753 754 /** 755 * Draws a grid line against the range axis. 756 * 757 * @param g2 the graphics device. 758 * @param plot the plot. 759 * @param axis the value axis. 760 * @param dataArea the area for plotting data (not yet adjusted for any 761 * 3D effect). 762 * @param value the value at which the grid line should be drawn. 763 * 764 * @see #drawDomainGridline(Graphics2D, CategoryPlot, Rectangle2D, double) 765 * 766 */ 767 public void drawRangeGridline(Graphics2D g2, 768 CategoryPlot plot, 769 ValueAxis axis, 770 Rectangle2D dataArea, 771 double value) { 772 773 Range range = axis.getRange(); 774 if (!range.contains(value)) { 775 return; 776 } 777 778 PlotOrientation orientation = plot.getOrientation(); 779 double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); 780 Line2D line = null; 781 if (orientation == PlotOrientation.HORIZONTAL) { 782 line = new Line2D.Double(v, dataArea.getMinY(), v, 783 dataArea.getMaxY()); 784 } 785 else if (orientation == PlotOrientation.VERTICAL) { 786 line = new Line2D.Double(dataArea.getMinX(), v, 787 dataArea.getMaxX(), v); 788 } 789 790 Paint paint = plot.getRangeGridlinePaint(); 791 if (paint == null) { 792 paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT; 793 } 794 g2.setPaint(paint); 795 796 Stroke stroke = plot.getRangeGridlineStroke(); 797 if (stroke == null) { 798 stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE; 799 } 800 g2.setStroke(stroke); 801 802 g2.draw(line); 803 804 } 805 806 /** 807 * Draws a marker for the domain axis. 808 * 809 * @param g2 the graphics device (not <code>null</code>). 810 * @param plot the plot (not <code>null</code>). 811 * @param axis the range axis (not <code>null</code>). 812 * @param marker the marker to be drawn (not <code>null</code>). 813 * @param dataArea the area inside the axes (not <code>null</code>). 814 * 815 * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker, 816 * Rectangle2D) 817 */ 818 public void drawDomainMarker(Graphics2D g2, 819 CategoryPlot plot, 820 CategoryAxis axis, 821 CategoryMarker marker, 822 Rectangle2D dataArea) { 823 824 Comparable category = marker.getKey(); 825 CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this)); 826 int columnIndex = dataset.getColumnIndex(category); 827 if (columnIndex < 0) { 828 return; 829 } 830 831 final Composite savedComposite = g2.getComposite(); 832 g2.setComposite(AlphaComposite.getInstance( 833 AlphaComposite.SRC_OVER, marker.getAlpha())); 834 835 PlotOrientation orientation = plot.getOrientation(); 836 Rectangle2D bounds = null; 837 if (marker.getDrawAsLine()) { 838 double v = axis.getCategoryMiddle(columnIndex, 839 dataset.getColumnCount(), dataArea, 840 plot.getDomainAxisEdge()); 841 Line2D line = null; 842 if (orientation == PlotOrientation.HORIZONTAL) { 843 line = new Line2D.Double(dataArea.getMinX(), v, 844 dataArea.getMaxX(), v); 845 } 846 else if (orientation == PlotOrientation.VERTICAL) { 847 line = new Line2D.Double(v, dataArea.getMinY(), v, 848 dataArea.getMaxY()); 849 } 850 g2.setPaint(marker.getPaint()); 851 g2.setStroke(marker.getStroke()); 852 g2.draw(line); 853 bounds = line.getBounds2D(); 854 } 855 else { 856 double v0 = axis.getCategoryStart(columnIndex, 857 dataset.getColumnCount(), dataArea, 858 plot.getDomainAxisEdge()); 859 double v1 = axis.getCategoryEnd(columnIndex, 860 dataset.getColumnCount(), dataArea, 861 plot.getDomainAxisEdge()); 862 Rectangle2D area = null; 863 if (orientation == PlotOrientation.HORIZONTAL) { 864 area = new Rectangle2D.Double(dataArea.getMinX(), v0, 865 dataArea.getWidth(), (v1 - v0)); 866 } 867 else if (orientation == PlotOrientation.VERTICAL) { 868 area = new Rectangle2D.Double(v0, dataArea.getMinY(), 869 (v1 - v0), dataArea.getHeight()); 870 } 871 g2.setPaint(marker.getPaint()); 872 g2.fill(area); 873 bounds = area; 874 } 875 876 String label = marker.getLabel(); 877 RectangleAnchor anchor = marker.getLabelAnchor(); 878 if (label != null) { 879 Font labelFont = marker.getLabelFont(); 880 g2.setFont(labelFont); 881 g2.setPaint(marker.getLabelPaint()); 882 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 883 g2, orientation, dataArea, bounds, marker.getLabelOffset(), 884 marker.getLabelOffsetType(), anchor); 885 TextUtilities.drawAlignedString(label, g2, 886 (float) coordinates.getX(), (float) coordinates.getY(), 887 marker.getLabelTextAnchor()); 888 } 889 g2.setComposite(savedComposite); 890 } 891 892 /** 893 * Draws a marker for the range axis. 894 * 895 * @param g2 the graphics device (not <code>null</code>). 896 * @param plot the plot (not <code>null</code>). 897 * @param axis the range axis (not <code>null</code>). 898 * @param marker the marker to be drawn (not <code>null</code>). 899 * @param dataArea the area inside the axes (not <code>null</code>). 900 * 901 * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis, 902 * CategoryMarker, Rectangle2D) 903 */ 904 public void drawRangeMarker(Graphics2D g2, 905 CategoryPlot plot, 906 ValueAxis axis, 907 Marker marker, 908 Rectangle2D dataArea) { 909 910 if (marker instanceof ValueMarker) { 911 ValueMarker vm = (ValueMarker) marker; 912 double value = vm.getValue(); 913 Range range = axis.getRange(); 914 915 if (!range.contains(value)) { 916 return; 917 } 918 919 final Composite savedComposite = g2.getComposite(); 920 g2.setComposite(AlphaComposite.getInstance( 921 AlphaComposite.SRC_OVER, marker.getAlpha())); 922 923 PlotOrientation orientation = plot.getOrientation(); 924 double v = axis.valueToJava2D(value, dataArea, 925 plot.getRangeAxisEdge()); 926 Line2D line = null; 927 if (orientation == PlotOrientation.HORIZONTAL) { 928 line = new Line2D.Double(v, dataArea.getMinY(), v, 929 dataArea.getMaxY()); 930 } 931 else if (orientation == PlotOrientation.VERTICAL) { 932 line = new Line2D.Double(dataArea.getMinX(), v, 933 dataArea.getMaxX(), v); 934 } 935 936 g2.setPaint(marker.getPaint()); 937 g2.setStroke(marker.getStroke()); 938 g2.draw(line); 939 940 String label = marker.getLabel(); 941 RectangleAnchor anchor = marker.getLabelAnchor(); 942 if (label != null) { 943 Font labelFont = marker.getLabelFont(); 944 g2.setFont(labelFont); 945 g2.setPaint(marker.getLabelPaint()); 946 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 947 g2, orientation, dataArea, line.getBounds2D(), 948 marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 949 anchor); 950 TextUtilities.drawAlignedString(label, g2, 951 (float) coordinates.getX(), (float) coordinates.getY(), 952 marker.getLabelTextAnchor()); 953 } 954 g2.setComposite(savedComposite); 955 } 956 else if (marker instanceof IntervalMarker) { 957 IntervalMarker im = (IntervalMarker) marker; 958 double start = im.getStartValue(); 959 double end = im.getEndValue(); 960 Range range = axis.getRange(); 961 if (!(range.intersects(start, end))) { 962 return; 963 } 964 965 final Composite savedComposite = g2.getComposite(); 966 g2.setComposite(AlphaComposite.getInstance( 967 AlphaComposite.SRC_OVER, marker.getAlpha())); 968 969 double start2d = axis.valueToJava2D(start, dataArea, 970 plot.getRangeAxisEdge()); 971 double end2d = axis.valueToJava2D(end, dataArea, 972 plot.getRangeAxisEdge()); 973 double low = Math.min(start2d, end2d); 974 double high = Math.max(start2d, end2d); 975 976 PlotOrientation orientation = plot.getOrientation(); 977 Rectangle2D rect = null; 978 if (orientation == PlotOrientation.HORIZONTAL) { 979 // clip left and right bounds to data area 980 low = Math.max(low, dataArea.getMinX()); 981 high = Math.min(high, dataArea.getMaxX()); 982 rect = new Rectangle2D.Double(low, 983 dataArea.getMinY(), high - low, 984 dataArea.getHeight()); 985 } 986 else if (orientation == PlotOrientation.VERTICAL) { 987 // clip top and bottom bounds to data area 988 low = Math.max(low, dataArea.getMinY()); 989 high = Math.min(high, dataArea.getMaxY()); 990 rect = new Rectangle2D.Double(dataArea.getMinX(), 991 low, dataArea.getWidth(), 992 high - low); 993 } 994 Paint p = marker.getPaint(); 995 if (p instanceof GradientPaint) { 996 GradientPaint gp = (GradientPaint) p; 997 GradientPaintTransformer t = im.getGradientPaintTransformer(); 998 if (t != null) { 999 gp = t.transform(gp, rect); 1000 } 1001 g2.setPaint(gp); 1002 } 1003 else { 1004 g2.setPaint(p); 1005 } 1006 g2.fill(rect); 1007 1008 // now draw the outlines, if visible... 1009 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 1010 if (orientation == PlotOrientation.VERTICAL) { 1011 Line2D line = new Line2D.Double(); 1012 double x0 = dataArea.getMinX(); 1013 double x1 = dataArea.getMaxX(); 1014 g2.setPaint(im.getOutlinePaint()); 1015 g2.setStroke(im.getOutlineStroke()); 1016 if (range.contains(start)) { 1017 line.setLine(x0, start2d, x1, start2d); 1018 g2.draw(line); 1019 } 1020 if (range.contains(end)) { 1021 line.setLine(x0, end2d, x1, end2d); 1022 g2.draw(line); 1023 } 1024 } 1025 else { // PlotOrientation.HORIZONTAL 1026 Line2D line = new Line2D.Double(); 1027 double y0 = dataArea.getMinY(); 1028 double y1 = dataArea.getMaxY(); 1029 g2.setPaint(im.getOutlinePaint()); 1030 g2.setStroke(im.getOutlineStroke()); 1031 if (range.contains(start)) { 1032 line.setLine(start2d, y0, start2d, y1); 1033 g2.draw(line); 1034 } 1035 if (range.contains(end)) { 1036 line.setLine(end2d, y0, end2d, y1); 1037 g2.draw(line); 1038 } 1039 } 1040 } 1041 1042 String label = marker.getLabel(); 1043 RectangleAnchor anchor = marker.getLabelAnchor(); 1044 if (label != null) { 1045 Font labelFont = marker.getLabelFont(); 1046 g2.setFont(labelFont); 1047 g2.setPaint(marker.getLabelPaint()); 1048 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1049 g2, orientation, dataArea, rect, 1050 marker.getLabelOffset(), marker.getLabelOffsetType(), 1051 anchor); 1052 TextUtilities.drawAlignedString(label, g2, 1053 (float) coordinates.getX(), (float) coordinates.getY(), 1054 marker.getLabelTextAnchor()); 1055 } 1056 g2.setComposite(savedComposite); 1057 } 1058 } 1059 1060 /** 1061 * Calculates the (x, y) coordinates for drawing the label for a marker on 1062 * the range axis. 1063 * 1064 * @param g2 the graphics device. 1065 * @param orientation the plot orientation. 1066 * @param dataArea the data area. 1067 * @param markerArea the rectangle surrounding the marker. 1068 * @param markerOffset the marker offset. 1069 * @param labelOffsetType the label offset type. 1070 * @param anchor the label anchor. 1071 * 1072 * @return The coordinates for drawing the marker label. 1073 */ 1074 protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, 1075 PlotOrientation orientation, 1076 Rectangle2D dataArea, 1077 Rectangle2D markerArea, 1078 RectangleInsets markerOffset, 1079 LengthAdjustmentType labelOffsetType, 1080 RectangleAnchor anchor) { 1081 1082 Rectangle2D anchorRect = null; 1083 if (orientation == PlotOrientation.HORIZONTAL) { 1084 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1085 LengthAdjustmentType.CONTRACT, labelOffsetType); 1086 } 1087 else if (orientation == PlotOrientation.VERTICAL) { 1088 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1089 labelOffsetType, LengthAdjustmentType.CONTRACT); 1090 } 1091 return RectangleAnchor.coordinates(anchorRect, anchor); 1092 1093 } 1094 1095 /** 1096 * Calculates the (x, y) coordinates for drawing a marker label. 1097 * 1098 * @param g2 the graphics device. 1099 * @param orientation the plot orientation. 1100 * @param dataArea the data area. 1101 * @param markerArea the rectangle surrounding the marker. 1102 * @param markerOffset the marker offset. 1103 * @param labelOffsetType the label offset type. 1104 * @param anchor the label anchor. 1105 * 1106 * @return The coordinates for drawing the marker label. 1107 */ 1108 protected Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, 1109 PlotOrientation orientation, 1110 Rectangle2D dataArea, 1111 Rectangle2D markerArea, 1112 RectangleInsets markerOffset, 1113 LengthAdjustmentType labelOffsetType, 1114 RectangleAnchor anchor) { 1115 1116 Rectangle2D anchorRect = null; 1117 if (orientation == PlotOrientation.HORIZONTAL) { 1118 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1119 labelOffsetType, LengthAdjustmentType.CONTRACT); 1120 } 1121 else if (orientation == PlotOrientation.VERTICAL) { 1122 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1123 LengthAdjustmentType.CONTRACT, labelOffsetType); 1124 } 1125 return RectangleAnchor.coordinates(anchorRect, anchor); 1126 1127 } 1128 1129 /** 1130 * Returns a legend item for a series. This default implementation will 1131 * return <code>null</code> if {@link #isSeriesVisible(int)} or 1132 * {@link #isSeriesVisibleInLegend(int)} returns <code>false</code>. 1133 * 1134 * @param datasetIndex the dataset index (zero-based). 1135 * @param series the series index (zero-based). 1136 * 1137 * @return The legend item (possibly <code>null</code>). 1138 * 1139 * @see #getLegendItems() 1140 */ 1141 public LegendItem getLegendItem(int datasetIndex, int series) { 1142 1143 CategoryPlot p = getPlot(); 1144 if (p == null) { 1145 return null; 1146 } 1147 1148 // check that a legend item needs to be displayed... 1149 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 1150 return null; 1151 } 1152 1153 CategoryDataset dataset = p.getDataset(datasetIndex); 1154 String label = this.legendItemLabelGenerator.generateLabel(dataset, 1155 series); 1156 String description = label; 1157 String toolTipText = null; 1158 if (this.legendItemToolTipGenerator != null) { 1159 toolTipText = this.legendItemToolTipGenerator.generateLabel( 1160 dataset, series); 1161 } 1162 String urlText = null; 1163 if (this.legendItemURLGenerator != null) { 1164 urlText = this.legendItemURLGenerator.generateLabel(dataset, 1165 series); 1166 } 1167 Shape shape = lookupSeriesShape(series); 1168 Paint paint = lookupSeriesPaint(series); 1169 Paint outlinePaint = lookupSeriesOutlinePaint(series); 1170 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 1171 1172 LegendItem item = new LegendItem(label, description, toolTipText, 1173 urlText, shape, paint, outlineStroke, outlinePaint); 1174 item.setSeriesKey(dataset.getRowKey(series)); 1175 item.setSeriesIndex(series); 1176 item.setDataset(dataset); 1177 item.setDatasetIndex(datasetIndex); 1178 return item; 1179 } 1180 1181 /** 1182 * Tests this renderer for equality with another object. 1183 * 1184 * @param obj the object. 1185 * 1186 * @return <code>true</code> or <code>false</code>. 1187 */ 1188 public boolean equals(Object obj) { 1189 1190 if (obj == this) { 1191 return true; 1192 } 1193 if (!(obj instanceof AbstractCategoryItemRenderer)) { 1194 return false; 1195 } 1196 AbstractCategoryItemRenderer that = (AbstractCategoryItemRenderer) obj; 1197 1198 if (!ObjectUtilities.equal(this.itemLabelGenerator, 1199 that.itemLabelGenerator)) { 1200 return false; 1201 } 1202 if (!ObjectUtilities.equal(this.itemLabelGeneratorList, 1203 that.itemLabelGeneratorList)) { 1204 return false; 1205 } 1206 if (!ObjectUtilities.equal(this.baseItemLabelGenerator, 1207 that.baseItemLabelGenerator)) { 1208 return false; 1209 } 1210 if (!ObjectUtilities.equal(this.toolTipGenerator, 1211 that.toolTipGenerator)) { 1212 return false; 1213 } 1214 if (!ObjectUtilities.equal(this.toolTipGeneratorList, 1215 that.toolTipGeneratorList)) { 1216 return false; 1217 } 1218 if (!ObjectUtilities.equal(this.baseToolTipGenerator, 1219 that.baseToolTipGenerator)) { 1220 return false; 1221 } 1222 if (!ObjectUtilities.equal(this.itemURLGenerator, 1223 that.itemURLGenerator)) { 1224 return false; 1225 } 1226 if (!ObjectUtilities.equal(this.itemURLGeneratorList, 1227 that.itemURLGeneratorList)) { 1228 return false; 1229 } 1230 if (!ObjectUtilities.equal(this.baseItemURLGenerator, 1231 that.baseItemURLGenerator)) { 1232 return false; 1233 } 1234 if (!ObjectUtilities.equal(this.legendItemLabelGenerator, 1235 that.legendItemLabelGenerator)) { 1236 return false; 1237 } 1238 if (!ObjectUtilities.equal(this.legendItemToolTipGenerator, 1239 that.legendItemToolTipGenerator)) { 1240 return false; 1241 } 1242 if (!ObjectUtilities.equal(this.legendItemURLGenerator, 1243 that.legendItemURLGenerator)) { 1244 return false; 1245 } 1246 return super.equals(obj); 1247 } 1248 1249 /** 1250 * Returns a hash code for the renderer. 1251 * 1252 * @return The hash code. 1253 */ 1254 public int hashCode() { 1255 int result = super.hashCode(); 1256 return result; 1257 } 1258 1259 /** 1260 * Returns the drawing supplier from the plot. 1261 * 1262 * @return The drawing supplier (possibly <code>null</code>). 1263 */ 1264 public DrawingSupplier getDrawingSupplier() { 1265 DrawingSupplier result = null; 1266 CategoryPlot cp = getPlot(); 1267 if (cp != null) { 1268 result = cp.getDrawingSupplier(); 1269 } 1270 return result; 1271 } 1272 1273 /** 1274 * Draws an item label. 1275 * 1276 * @param g2 the graphics device. 1277 * @param orientation the orientation. 1278 * @param dataset the dataset. 1279 * @param row the row. 1280 * @param column the column. 1281 * @param x the x coordinate (in Java2D space). 1282 * @param y the y coordinate (in Java2D space). 1283 * @param negative indicates a negative value (which affects the item 1284 * label position). 1285 */ 1286 protected void drawItemLabel(Graphics2D g2, 1287 PlotOrientation orientation, 1288 CategoryDataset dataset, 1289 int row, int column, 1290 double x, double y, 1291 boolean negative) { 1292 1293 CategoryItemLabelGenerator generator 1294 = getItemLabelGenerator(row, column); 1295 if (generator != null) { 1296 Font labelFont = getItemLabelFont(row, column); 1297 Paint paint = getItemLabelPaint(row, column); 1298 g2.setFont(labelFont); 1299 g2.setPaint(paint); 1300 String label = generator.generateLabel(dataset, row, column); 1301 ItemLabelPosition position = null; 1302 if (!negative) { 1303 position = getPositiveItemLabelPosition(row, column); 1304 } 1305 else { 1306 position = getNegativeItemLabelPosition(row, column); 1307 } 1308 Point2D anchorPoint = calculateLabelAnchorPoint( 1309 position.getItemLabelAnchor(), x, y, orientation); 1310 TextUtilities.drawRotatedString(label, g2, 1311 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 1312 position.getTextAnchor(), 1313 position.getAngle(), position.getRotationAnchor()); 1314 } 1315 1316 } 1317 1318 /** 1319 * Returns an independent copy of the renderer. The <code>plot</code> 1320 * reference is shallow copied. 1321 * 1322 * @return A clone. 1323 * 1324 * @throws CloneNotSupportedException can be thrown if one of the objects 1325 * belonging to the renderer does not support cloning (for example, 1326 * an item label generator). 1327 */ 1328 public Object clone() throws CloneNotSupportedException { 1329 1330 AbstractCategoryItemRenderer clone 1331 = (AbstractCategoryItemRenderer) super.clone(); 1332 1333 if (this.itemLabelGenerator != null) { 1334 if (this.itemLabelGenerator instanceof PublicCloneable) { 1335 PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator; 1336 clone.itemLabelGenerator 1337 = (CategoryItemLabelGenerator) pc.clone(); 1338 } 1339 else { 1340 throw new CloneNotSupportedException( 1341 "ItemLabelGenerator not cloneable."); 1342 } 1343 } 1344 1345 if (this.itemLabelGeneratorList != null) { 1346 clone.itemLabelGeneratorList 1347 = (ObjectList) this.itemLabelGeneratorList.clone(); 1348 } 1349 1350 if (this.baseItemLabelGenerator != null) { 1351 if (this.baseItemLabelGenerator instanceof PublicCloneable) { 1352 PublicCloneable pc 1353 = (PublicCloneable) this.baseItemLabelGenerator; 1354 clone.baseItemLabelGenerator 1355 = (CategoryItemLabelGenerator) pc.clone(); 1356 } 1357 else { 1358 throw new CloneNotSupportedException( 1359 "ItemLabelGenerator not cloneable."); 1360 } 1361 } 1362 1363 if (this.toolTipGenerator != null) { 1364 if (this.toolTipGenerator instanceof PublicCloneable) { 1365 PublicCloneable pc = (PublicCloneable) this.toolTipGenerator; 1366 clone.toolTipGenerator = (CategoryToolTipGenerator) pc.clone(); 1367 } 1368 else { 1369 throw new CloneNotSupportedException( 1370 "Tool tip generator not cloneable."); 1371 } 1372 } 1373 1374 if (this.toolTipGeneratorList != null) { 1375 clone.toolTipGeneratorList 1376 = (ObjectList) this.toolTipGeneratorList.clone(); 1377 } 1378 1379 if (this.baseToolTipGenerator != null) { 1380 if (this.baseToolTipGenerator instanceof PublicCloneable) { 1381 PublicCloneable pc 1382 = (PublicCloneable) this.baseToolTipGenerator; 1383 clone.baseToolTipGenerator 1384 = (CategoryToolTipGenerator) pc.clone(); 1385 } 1386 else { 1387 throw new CloneNotSupportedException( 1388 "Base tool tip generator not cloneable."); 1389 } 1390 } 1391 1392 if (this.itemURLGenerator != null) { 1393 if (this.itemURLGenerator instanceof PublicCloneable) { 1394 PublicCloneable pc = (PublicCloneable) this.itemURLGenerator; 1395 clone.itemURLGenerator = (CategoryURLGenerator) pc.clone(); 1396 } 1397 else { 1398 throw new CloneNotSupportedException( 1399 "Item URL generator not cloneable."); 1400 } 1401 } 1402 1403 if (this.itemURLGeneratorList != null) { 1404 clone.itemURLGeneratorList 1405 = (ObjectList) this.itemURLGeneratorList.clone(); 1406 } 1407 1408 if (this.baseItemURLGenerator != null) { 1409 if (this.baseItemURLGenerator instanceof PublicCloneable) { 1410 PublicCloneable pc 1411 = (PublicCloneable) this.baseItemURLGenerator; 1412 clone.baseItemURLGenerator = (CategoryURLGenerator) pc.clone(); 1413 } 1414 else { 1415 throw new CloneNotSupportedException( 1416 "Base item URL generator not cloneable."); 1417 } 1418 } 1419 1420 if (this.legendItemLabelGenerator instanceof PublicCloneable) { 1421 clone.legendItemLabelGenerator = (CategorySeriesLabelGenerator) 1422 ObjectUtilities.clone(this.legendItemLabelGenerator); 1423 } 1424 if (this.legendItemToolTipGenerator instanceof PublicCloneable) { 1425 clone.legendItemToolTipGenerator = (CategorySeriesLabelGenerator) 1426 ObjectUtilities.clone(this.legendItemToolTipGenerator); 1427 } 1428 if (this.legendItemURLGenerator instanceof PublicCloneable) { 1429 clone.legendItemURLGenerator = (CategorySeriesLabelGenerator) 1430 ObjectUtilities.clone(this.legendItemURLGenerator); 1431 } 1432 return clone; 1433 } 1434 1435 /** 1436 * Returns a domain axis for a plot. 1437 * 1438 * @param plot the plot. 1439 * @param index the axis index. 1440 * 1441 * @return A domain axis. 1442 */ 1443 protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) { 1444 CategoryAxis result = plot.getDomainAxis(index); 1445 if (result == null) { 1446 result = plot.getDomainAxis(); 1447 } 1448 return result; 1449 } 1450 1451 /** 1452 * Returns a range axis for a plot. 1453 * 1454 * @param plot the plot. 1455 * @param index the axis index. 1456 * 1457 * @return A range axis. 1458 */ 1459 protected ValueAxis getRangeAxis(CategoryPlot plot, int index) { 1460 ValueAxis result = plot.getRangeAxis(index); 1461 if (result == null) { 1462 result = plot.getRangeAxis(); 1463 } 1464 return result; 1465 } 1466 1467 /** 1468 * Returns a (possibly empty) collection of legend items for the series 1469 * that this renderer is responsible for drawing. 1470 * 1471 * @return The legend item collection (never <code>null</code>). 1472 * 1473 * @see #getLegendItem(int, int) 1474 */ 1475 public LegendItemCollection getLegendItems() { 1476 if (this.plot == null) { 1477 return new LegendItemCollection(); 1478 } 1479 LegendItemCollection result = new LegendItemCollection(); 1480 int index = this.plot.getIndexOf(this); 1481 CategoryDataset dataset = this.plot.getDataset(index); 1482 if (dataset != null) { 1483 int seriesCount = dataset.getRowCount(); 1484 for (int i = 0; i < seriesCount; i++) { 1485 if (isSeriesVisibleInLegend(i)) { 1486 LegendItem item = getLegendItem(index, i); 1487 if (item != null) { 1488 result.add(item); 1489 } 1490 } 1491 } 1492 1493 } 1494 return result; 1495 } 1496 1497 /** 1498 * Returns the legend item label generator. 1499 * 1500 * @return The label generator (never <code>null</code>). 1501 * 1502 * @see #setLegendItemLabelGenerator(CategorySeriesLabelGenerator) 1503 */ 1504 public CategorySeriesLabelGenerator getLegendItemLabelGenerator() { 1505 return this.legendItemLabelGenerator; 1506 } 1507 1508 /** 1509 * Sets the legend item label generator and sends a 1510 * {@link RendererChangeEvent} to all registered listeners. 1511 * 1512 * @param generator the generator (<code>null</code> not permitted). 1513 * 1514 * @see #getLegendItemLabelGenerator() 1515 */ 1516 public void setLegendItemLabelGenerator( 1517 CategorySeriesLabelGenerator generator) { 1518 if (generator == null) { 1519 throw new IllegalArgumentException("Null 'generator' argument."); 1520 } 1521 this.legendItemLabelGenerator = generator; 1522 notifyListeners(new RendererChangeEvent(this)); 1523 } 1524 1525 /** 1526 * Returns the legend item tool tip generator. 1527 * 1528 * @return The tool tip generator (possibly <code>null</code>). 1529 * 1530 * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) 1531 */ 1532 public CategorySeriesLabelGenerator getLegendItemToolTipGenerator() { 1533 return this.legendItemToolTipGenerator; 1534 } 1535 1536 /** 1537 * Sets the legend item tool tip generator and sends a 1538 * {@link RendererChangeEvent} to all registered listeners. 1539 * 1540 * @param generator the generator (<code>null</code> permitted). 1541 * 1542 * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) 1543 */ 1544 public void setLegendItemToolTipGenerator( 1545 CategorySeriesLabelGenerator generator) { 1546 this.legendItemToolTipGenerator = generator; 1547 notifyListeners(new RendererChangeEvent(this)); 1548 } 1549 1550 /** 1551 * Returns the legend item URL generator. 1552 * 1553 * @return The URL generator (possibly <code>null</code>). 1554 * 1555 * @see #setLegendItemURLGenerator(CategorySeriesLabelGenerator) 1556 */ 1557 public CategorySeriesLabelGenerator getLegendItemURLGenerator() { 1558 return this.legendItemURLGenerator; 1559 } 1560 1561 /** 1562 * Sets the legend item URL generator and sends a 1563 * {@link RendererChangeEvent} to all registered listeners. 1564 * 1565 * @param generator the generator (<code>null</code> permitted). 1566 * 1567 * @see #getLegendItemURLGenerator() 1568 */ 1569 public void setLegendItemURLGenerator( 1570 CategorySeriesLabelGenerator generator) { 1571 this.legendItemURLGenerator = generator; 1572 notifyListeners(new RendererChangeEvent(this)); 1573 } 1574 1575 /** 1576 * Adds an entity with the specified hotspot. 1577 * 1578 * @param entities the entity collection. 1579 * @param dataset the dataset. 1580 * @param row the row index. 1581 * @param column the column index. 1582 * @param hotspot the hotspot. 1583 */ 1584 protected void addItemEntity(EntityCollection entities, 1585 CategoryDataset dataset, int row, int column, 1586 Shape hotspot) { 1587 1588 String tip = null; 1589 CategoryToolTipGenerator tipster = getToolTipGenerator(row, column); 1590 if (tipster != null) { 1591 tip = tipster.generateToolTip(dataset, row, column); 1592 } 1593 String url = null; 1594 CategoryURLGenerator urlster = getItemURLGenerator(row, column); 1595 if (urlster != null) { 1596 url = urlster.generateURL(dataset, row, column); 1597 } 1598 CategoryItemEntity entity = new CategoryItemEntity(hotspot, tip, url, 1599 dataset, dataset.getRowKey(row), dataset.getColumnKey(column)); 1600 entities.add(entity); 1601 1602 } 1603 1604 1605}