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