Source for org.jfree.chart.renderer.category.AbstractCategoryItemRenderer

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