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