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