0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ---------------------------------
0028: * AbstractCategoryItemRenderer.java
0029: * ---------------------------------
0030: * (C) Copyright 2002-2007, by Object Refinery Limited.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Richard Atkinson;
0034: *
0035: * $Id: AbstractCategoryItemRenderer.java,v 1.17.2.23 2007/06/01 15:12:14 mungady Exp $
0036: *
0037: * Changes:
0038: * --------
0039: * 29-May-2002 : Version 1 (DG);
0040: * 06-Jun-2002 : Added accessor methods for the tool tip generator (DG);
0041: * 11-Jun-2002 : Made constructors protected (DG);
0042: * 26-Jun-2002 : Added axis to initialise method (DG);
0043: * 05-Aug-2002 : Added urlGenerator member variable plus accessors (RA);
0044: * 22-Aug-2002 : Added categoriesPaint attribute, based on code submitted by
0045: * Janet Banks. This can be used when there is only one series,
0046: * and you want each category item to have a different color (DG);
0047: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
0048: * 29-Oct-2002 : Fixed bug where background image for plot was not being
0049: * drawn (DG);
0050: * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG);
0051: * 26-Nov 2002 : Replaced the isStacked() method with getRangeType() (DG);
0052: * 09-Jan-2003 : Renamed grid-line methods (DG);
0053: * 17-Jan-2003 : Moved plot classes into separate package (DG);
0054: * 25-Mar-2003 : Implemented Serializable (DG);
0055: * 12-May-2003 : Modified to take into account the plot orientation (DG);
0056: * 12-Aug-2003 : Very minor javadoc corrections (DB)
0057: * 13-Aug-2003 : Implemented Cloneable (DG);
0058: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0059: * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
0060: * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
0061: * 11-Feb-2004 : Modified labelling for markers (DG);
0062: * 12-Feb-2004 : Updated clone() method (DG);
0063: * 15-Apr-2004 : Created a new CategoryToolTipGenerator interface (DG);
0064: * 05-May-2004 : Fixed bug (948310) where interval markers extend outside axis
0065: * range (DG);
0066: * 14-Jun-2004 : Fixed bug in drawRangeMarker() method - now uses 'paint' and
0067: * 'stroke' rather than 'outlinePaint' and 'outlineStroke' (DG);
0068: * 15-Jun-2004 : Interval markers can now use GradientPaint (DG);
0069: * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
0070: * --> TextUtilities (DG);
0071: * 01-Oct-2004 : Fixed bug 1029697, problem with label alignment in
0072: * drawRangeMarker() method (DG);
0073: * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
0074: * 21-Jan-2005 : Modified return type of calculateRangeMarkerTextAnchorPoint()
0075: * method (DG);
0076: * 08-Mar-2005 : Fixed positioning of marker labels (DG);
0077: * 20-Apr-2005 : Added legend label, tooltip and URL generators (DG);
0078: * 01-Jun-2005 : Handle one dimension of the marker label adjustment
0079: * automatically (DG);
0080: * 09-Jun-2005 : Added utility method for adding an item entity (DG);
0081: * ------------- JFREECHART 1.0.x ---------------------------------------------
0082: * 01-Mar-2006 : Updated getLegendItems() to check seriesVisibleInLegend
0083: * flags (DG);
0084: * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
0085: * 23-Oct-2006 : Draw outlines for interval markers (DG);
0086: * 24-Oct-2006 : Respect alpha setting in markers, as suggested by Sergei
0087: * Ivanov in patch 1567843 (DG);
0088: * 30-Nov-2006 : Added a check for series visibility in the getLegendItem()
0089: * method (DG);
0090: * 07-Dec-2006 : Fix for equals() method (DG);
0091: * 22-Feb-2007 : Added createState() method (DG);
0092: * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to
0093: * Sergei Ivanov) (DG);
0094: * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
0095: * itemLabelGenerator, toolTipGenerator and itemURLGenerator
0096: * override fields (DG);
0097: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
0098: *
0099: */
0100:
0101: package org.jfree.chart.renderer.category;
0102:
0103: import java.awt.AlphaComposite;
0104: import java.awt.Composite;
0105: import java.awt.Font;
0106: import java.awt.GradientPaint;
0107: import java.awt.Graphics2D;
0108: import java.awt.Paint;
0109: import java.awt.Shape;
0110: import java.awt.Stroke;
0111: import java.awt.geom.Line2D;
0112: import java.awt.geom.Point2D;
0113: import java.awt.geom.Rectangle2D;
0114: import java.io.Serializable;
0115:
0116: import org.jfree.chart.LegendItem;
0117: import org.jfree.chart.LegendItemCollection;
0118: import org.jfree.chart.axis.CategoryAxis;
0119: import org.jfree.chart.axis.ValueAxis;
0120: import org.jfree.chart.entity.CategoryItemEntity;
0121: import org.jfree.chart.entity.EntityCollection;
0122: import org.jfree.chart.event.RendererChangeEvent;
0123: import org.jfree.chart.labels.CategoryItemLabelGenerator;
0124: import org.jfree.chart.labels.CategorySeriesLabelGenerator;
0125: import org.jfree.chart.labels.CategoryToolTipGenerator;
0126: import org.jfree.chart.labels.ItemLabelPosition;
0127: import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator;
0128: import org.jfree.chart.plot.CategoryMarker;
0129: import org.jfree.chart.plot.CategoryPlot;
0130: import org.jfree.chart.plot.DrawingSupplier;
0131: import org.jfree.chart.plot.IntervalMarker;
0132: import org.jfree.chart.plot.Marker;
0133: import org.jfree.chart.plot.PlotOrientation;
0134: import org.jfree.chart.plot.PlotRenderingInfo;
0135: import org.jfree.chart.plot.ValueMarker;
0136: import org.jfree.chart.renderer.AbstractRenderer;
0137: import org.jfree.chart.urls.CategoryURLGenerator;
0138: import org.jfree.data.Range;
0139: import org.jfree.data.category.CategoryDataset;
0140: import org.jfree.data.general.DatasetUtilities;
0141: import org.jfree.text.TextUtilities;
0142: import org.jfree.ui.GradientPaintTransformer;
0143: import org.jfree.ui.LengthAdjustmentType;
0144: import org.jfree.ui.RectangleAnchor;
0145: import org.jfree.ui.RectangleInsets;
0146: import org.jfree.util.ObjectList;
0147: import org.jfree.util.ObjectUtilities;
0148: import org.jfree.util.PublicCloneable;
0149:
0150: /**
0151: * An abstract base class that you can use to implement a new
0152: * {@link CategoryItemRenderer}. When you create a new
0153: * {@link CategoryItemRenderer} you are not required to extend this class,
0154: * but it makes the job easier.
0155: */
0156: public abstract class AbstractCategoryItemRenderer extends
0157: AbstractRenderer implements CategoryItemRenderer, Cloneable,
0158: PublicCloneable, Serializable {
0159:
0160: /** For serialization. */
0161: private static final long serialVersionUID = 1247553218442497391L;
0162:
0163: /** The plot that the renderer is assigned to. */
0164: private CategoryPlot plot;
0165:
0166: /**
0167: * The item label generator for ALL series.
0168: *
0169: * @deprecated This field is redundant and deprecated as of version 1.0.6.
0170: */
0171: private CategoryItemLabelGenerator itemLabelGenerator;
0172:
0173: /** A list of item label generators (one per series). */
0174: private ObjectList itemLabelGeneratorList;
0175:
0176: /** The base item label generator. */
0177: private CategoryItemLabelGenerator baseItemLabelGenerator;
0178:
0179: /**
0180: * The tool tip generator for ALL series.
0181: *
0182: * @deprecated This field is redundant and deprecated as of version 1.0.6.
0183: */
0184: private CategoryToolTipGenerator toolTipGenerator;
0185:
0186: /** A list of tool tip generators (one per series). */
0187: private ObjectList toolTipGeneratorList;
0188:
0189: /** The base tool tip generator. */
0190: private CategoryToolTipGenerator baseToolTipGenerator;
0191:
0192: /**
0193: * The URL generator.
0194: *
0195: * @deprecated This field is redundant and deprecated as of version 1.0.6.
0196: */
0197: private CategoryURLGenerator itemURLGenerator;
0198:
0199: /** A list of item label generators (one per series). */
0200: private ObjectList itemURLGeneratorList;
0201:
0202: /** The base item label generator. */
0203: private CategoryURLGenerator baseItemURLGenerator;
0204:
0205: /** The legend item label generator. */
0206: private CategorySeriesLabelGenerator legendItemLabelGenerator;
0207:
0208: /** The legend item tool tip generator. */
0209: private CategorySeriesLabelGenerator legendItemToolTipGenerator;
0210:
0211: /** The legend item URL generator. */
0212: private CategorySeriesLabelGenerator legendItemURLGenerator;
0213:
0214: /** The number of rows in the dataset (temporary record). */
0215: private transient int rowCount;
0216:
0217: /** The number of columns in the dataset (temporary record). */
0218: private transient int columnCount;
0219:
0220: /**
0221: * Creates a new renderer with no tool tip generator and no URL generator.
0222: * The defaults (no tool tip or URL generators) have been chosen to
0223: * minimise the processing required to generate a default chart. If you
0224: * require tool tips or URLs, then you can easily add the required
0225: * generators.
0226: */
0227: protected AbstractCategoryItemRenderer() {
0228: this .itemLabelGenerator = null;
0229: this .itemLabelGeneratorList = new ObjectList();
0230: this .toolTipGenerator = null;
0231: this .toolTipGeneratorList = new ObjectList();
0232: this .itemURLGenerator = null;
0233: this .itemURLGeneratorList = new ObjectList();
0234: this .legendItemLabelGenerator = new StandardCategorySeriesLabelGenerator();
0235: }
0236:
0237: /**
0238: * Returns the number of passes through the dataset required by the
0239: * renderer. This method returns <code>1</code>, subclasses should
0240: * override if they need more passes.
0241: *
0242: * @return The pass count.
0243: */
0244: public int getPassCount() {
0245: return 1;
0246: }
0247:
0248: /**
0249: * Returns the plot that the renderer has been assigned to (where
0250: * <code>null</code> indicates that the renderer is not currently assigned
0251: * to a plot).
0252: *
0253: * @return The plot (possibly <code>null</code>).
0254: *
0255: * @see #setPlot(CategoryPlot)
0256: */
0257: public CategoryPlot getPlot() {
0258: return this .plot;
0259: }
0260:
0261: /**
0262: * Sets the plot that the renderer has been assigned to. This method is
0263: * usually called by the {@link CategoryPlot}, in normal usage you
0264: * shouldn't need to call this method directly.
0265: *
0266: * @param plot the plot (<code>null</code> not permitted).
0267: *
0268: * @see #getPlot()
0269: */
0270: public void setPlot(CategoryPlot plot) {
0271: if (plot == null) {
0272: throw new IllegalArgumentException("Null 'plot' argument.");
0273: }
0274: this .plot = plot;
0275: }
0276:
0277: // ITEM LABEL GENERATOR
0278:
0279: /**
0280: * Returns the item label generator for a data item. This implementation
0281: * simply passes control to the {@link #getSeriesItemLabelGenerator(int)}
0282: * method. If, for some reason, you want a different generator for
0283: * individual items, you can override this method.
0284: *
0285: * @param row the row index (zero based).
0286: * @param column the column index (zero based).
0287: *
0288: * @return The generator (possibly <code>null</code>).
0289: */
0290: public CategoryItemLabelGenerator getItemLabelGenerator(int row,
0291: int column) {
0292: return getSeriesItemLabelGenerator(row);
0293: }
0294:
0295: /**
0296: * Returns the item label generator for a series.
0297: *
0298: * @param series the series index (zero based).
0299: *
0300: * @return The generator (possibly <code>null</code>).
0301: *
0302: * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator)
0303: */
0304: public CategoryItemLabelGenerator getSeriesItemLabelGenerator(
0305: int series) {
0306:
0307: // return the generator for ALL series, if there is one...
0308: if (this .itemLabelGenerator != null) {
0309: return this .itemLabelGenerator;
0310: }
0311:
0312: // otherwise look up the generator table
0313: CategoryItemLabelGenerator generator = (CategoryItemLabelGenerator) this .itemLabelGeneratorList
0314: .get(series);
0315: if (generator == null) {
0316: generator = this .baseItemLabelGenerator;
0317: }
0318: return generator;
0319:
0320: }
0321:
0322: /**
0323: * Sets the item label generator for ALL series and sends a
0324: * {@link RendererChangeEvent} to all registered listeners.
0325: *
0326: * @param generator the generator (<code>null</code> permitted).
0327: *
0328: * @deprecated This method should no longer be used (as of version 1.0.6).
0329: * It is sufficient to rely on {@link #setSeriesItemLabelGenerator(int,
0330: * CategoryItemLabelGenerator)} and
0331: * {@link #setBaseItemLabelGenerator(CategoryItemLabelGenerator)}.
0332: */
0333: public void setItemLabelGenerator(
0334: CategoryItemLabelGenerator generator) {
0335: this .itemLabelGenerator = generator;
0336: notifyListeners(new RendererChangeEvent(this ));
0337: }
0338:
0339: /**
0340: * Sets the item label generator for a series and sends a
0341: * {@link RendererChangeEvent} to all registered listeners.
0342: *
0343: * @param series the series index (zero based).
0344: * @param generator the generator (<code>null</code> permitted).
0345: *
0346: * @see #getSeriesItemLabelGenerator(int)
0347: */
0348: public void setSeriesItemLabelGenerator(int series,
0349: CategoryItemLabelGenerator generator) {
0350: this .itemLabelGeneratorList.set(series, generator);
0351: notifyListeners(new RendererChangeEvent(this ));
0352: }
0353:
0354: /**
0355: * Returns the base item label generator.
0356: *
0357: * @return The generator (possibly <code>null</code>).
0358: *
0359: * @see #setBaseItemLabelGenerator(CategoryItemLabelGenerator)
0360: */
0361: public CategoryItemLabelGenerator getBaseItemLabelGenerator() {
0362: return this .baseItemLabelGenerator;
0363: }
0364:
0365: /**
0366: * Sets the base item label generator and sends a
0367: * {@link RendererChangeEvent} to all registered listeners.
0368: *
0369: * @param generator the generator (<code>null</code> permitted).
0370: *
0371: * @see #getBaseItemLabelGenerator()
0372: */
0373: public void setBaseItemLabelGenerator(
0374: CategoryItemLabelGenerator generator) {
0375: this .baseItemLabelGenerator = generator;
0376: notifyListeners(new RendererChangeEvent(this ));
0377: }
0378:
0379: // TOOL TIP GENERATOR
0380:
0381: /**
0382: * Returns the tool tip generator that should be used for the specified
0383: * item. This method looks up the generator using the "three-layer"
0384: * approach outlined in the general description of this interface. You
0385: * can override this method if you want to return a different generator per
0386: * item.
0387: *
0388: * @param row the row index (zero-based).
0389: * @param column the column index (zero-based).
0390: *
0391: * @return The generator (possibly <code>null</code>).
0392: */
0393: public CategoryToolTipGenerator getToolTipGenerator(int row,
0394: int column) {
0395:
0396: CategoryToolTipGenerator result = null;
0397: if (this .toolTipGenerator != null) {
0398: result = this .toolTipGenerator;
0399: } else {
0400: result = getSeriesToolTipGenerator(row);
0401: if (result == null) {
0402: result = this .baseToolTipGenerator;
0403: }
0404: }
0405: return result;
0406: }
0407:
0408: /**
0409: * Returns the tool tip generator that will be used for ALL items in the
0410: * dataset (the "layer 0" generator).
0411: *
0412: * @return A tool tip generator (possibly <code>null</code>).
0413: *
0414: * @see #setToolTipGenerator(CategoryToolTipGenerator)
0415: *
0416: * @deprecated This method should no longer be used (as of version 1.0.6).
0417: * It is sufficient to rely on {@link #getSeriesToolTipGenerator(int)}
0418: * and {@link #getBaseToolTipGenerator()}.
0419: */
0420: public CategoryToolTipGenerator getToolTipGenerator() {
0421: return this .toolTipGenerator;
0422: }
0423:
0424: /**
0425: * Sets the tool tip generator for ALL series and sends a
0426: * {@link org.jfree.chart.event.RendererChangeEvent} to all registered
0427: * listeners.
0428: *
0429: * @param generator the generator (<code>null</code> permitted).
0430: *
0431: * @see #getToolTipGenerator()
0432: *
0433: * @deprecated This method should no longer be used (as of version 1.0.6).
0434: * It is sufficient to rely on {@link #setSeriesToolTipGenerator(int,
0435: * CategoryToolTipGenerator)} and
0436: * {@link #setBaseToolTipGenerator(CategoryToolTipGenerator)}.
0437: */
0438: public void setToolTipGenerator(CategoryToolTipGenerator generator) {
0439: this .toolTipGenerator = generator;
0440: notifyListeners(new RendererChangeEvent(this ));
0441: }
0442:
0443: /**
0444: * Returns the tool tip generator for the specified series (a "layer 1"
0445: * generator).
0446: *
0447: * @param series the series index (zero-based).
0448: *
0449: * @return The tool tip generator (possibly <code>null</code>).
0450: *
0451: * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator)
0452: */
0453: public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) {
0454: return (CategoryToolTipGenerator) this .toolTipGeneratorList
0455: .get(series);
0456: }
0457:
0458: /**
0459: * Sets the tool tip generator for a series and sends a
0460: * {@link org.jfree.chart.event.RendererChangeEvent} to all registered
0461: * listeners.
0462: *
0463: * @param series the series index (zero-based).
0464: * @param generator the generator (<code>null</code> permitted).
0465: *
0466: * @see #getSeriesToolTipGenerator(int)
0467: */
0468: public void setSeriesToolTipGenerator(int series,
0469: CategoryToolTipGenerator generator) {
0470: this .toolTipGeneratorList.set(series, generator);
0471: notifyListeners(new RendererChangeEvent(this ));
0472: }
0473:
0474: /**
0475: * Returns the base tool tip generator (the "layer 2" generator).
0476: *
0477: * @return The tool tip generator (possibly <code>null</code>).
0478: *
0479: * @see #setBaseToolTipGenerator(CategoryToolTipGenerator)
0480: */
0481: public CategoryToolTipGenerator getBaseToolTipGenerator() {
0482: return this .baseToolTipGenerator;
0483: }
0484:
0485: /**
0486: * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
0487: * to all registered listeners.
0488: *
0489: * @param generator the generator (<code>null</code> permitted).
0490: *
0491: * @see #getBaseToolTipGenerator()
0492: */
0493: public void setBaseToolTipGenerator(
0494: CategoryToolTipGenerator generator) {
0495: this .baseToolTipGenerator = generator;
0496: notifyListeners(new RendererChangeEvent(this ));
0497: }
0498:
0499: // URL GENERATOR
0500:
0501: /**
0502: * Returns the URL generator for a data item. This method just calls the
0503: * getSeriesItemURLGenerator method, but you can override this behaviour if
0504: * you want to.
0505: *
0506: * @param row the row index (zero based).
0507: * @param column the column index (zero based).
0508: *
0509: * @return The URL generator.
0510: */
0511: public CategoryURLGenerator getItemURLGenerator(int row, int column) {
0512: return getSeriesItemURLGenerator(row);
0513: }
0514:
0515: /**
0516: * Returns the URL generator for a series.
0517: *
0518: * @param series the series index (zero based).
0519: *
0520: * @return The URL generator for the series.
0521: *
0522: * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator)
0523: */
0524: public CategoryURLGenerator getSeriesItemURLGenerator(int series) {
0525:
0526: // return the generator for ALL series, if there is one...
0527: if (this .itemURLGenerator != null) {
0528: return this .itemURLGenerator;
0529: }
0530:
0531: // otherwise look up the generator table
0532: CategoryURLGenerator generator = (CategoryURLGenerator) this .itemURLGeneratorList
0533: .get(series);
0534: if (generator == null) {
0535: generator = this .baseItemURLGenerator;
0536: }
0537: return generator;
0538:
0539: }
0540:
0541: /**
0542: * Sets the item URL generator for ALL series.
0543: *
0544: * @param generator the generator.
0545: *
0546: * @deprecated This method should no longer be used (as of version 1.0.6).
0547: * It is sufficient to rely on {@link #setSeriesItemURLGenerator(int,
0548: * CategoryURLGenerator)} and
0549: * {@link #setBaseItemURLGenerator(CategoryURLGenerator)}.
0550: */
0551: public void setItemURLGenerator(CategoryURLGenerator generator) {
0552: this .itemURLGenerator = generator;
0553: notifyListeners(new RendererChangeEvent(this ));
0554: }
0555:
0556: /**
0557: * Sets the URL generator for a series.
0558: *
0559: * @param series the series index (zero based).
0560: * @param generator the generator.
0561: *
0562: * @see #getSeriesItemURLGenerator(int)
0563: */
0564: public void setSeriesItemURLGenerator(int series,
0565: CategoryURLGenerator generator) {
0566: this .itemURLGeneratorList.set(series, generator);
0567: notifyListeners(new RendererChangeEvent(this ));
0568: }
0569:
0570: /**
0571: * Returns the base item URL generator.
0572: *
0573: * @return The item URL generator.
0574: *
0575: * @see #setBaseItemURLGenerator(CategoryURLGenerator)
0576: */
0577: public CategoryURLGenerator getBaseItemURLGenerator() {
0578: return this .baseItemURLGenerator;
0579: }
0580:
0581: /**
0582: * Sets the base item URL generator.
0583: *
0584: * @param generator the item URL generator.
0585: *
0586: * @see #getBaseItemURLGenerator()
0587: */
0588: public void setBaseItemURLGenerator(CategoryURLGenerator generator) {
0589: this .baseItemURLGenerator = generator;
0590: notifyListeners(new RendererChangeEvent(this ));
0591: }
0592:
0593: /**
0594: * Returns the number of rows in the dataset. This value is updated in the
0595: * {@link AbstractCategoryItemRenderer#initialise} method.
0596: *
0597: * @return The row count.
0598: */
0599: public int getRowCount() {
0600: return this .rowCount;
0601: }
0602:
0603: /**
0604: * Returns the number of columns in the dataset. This value is updated in
0605: * the {@link AbstractCategoryItemRenderer#initialise} method.
0606: *
0607: * @return The column count.
0608: */
0609: public int getColumnCount() {
0610: return this .columnCount;
0611: }
0612:
0613: /**
0614: * Creates a new state instance---this method is called from the
0615: * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int,
0616: * PlotRenderingInfo)} method. Subclasses can override this method if
0617: * they need to use a subclass of {@link CategoryItemRendererState}.
0618: *
0619: * @param info collects plot rendering info (<code>null</code> permitted).
0620: *
0621: * @return The new state instance (never <code>null</code>).
0622: *
0623: * @since 1.0.5
0624: */
0625: protected CategoryItemRendererState createState(
0626: PlotRenderingInfo info) {
0627: return new CategoryItemRendererState(info);
0628: }
0629:
0630: /**
0631: * Initialises the renderer and returns a state object that will be used
0632: * for the remainder of the drawing process for a single chart. The state
0633: * object allows for the fact that the renderer may be used simultaneously
0634: * by multiple threads (each thread will work with a separate state object).
0635: *
0636: * @param g2 the graphics device.
0637: * @param dataArea the data area.
0638: * @param plot the plot.
0639: * @param rendererIndex the renderer index.
0640: * @param info an object for returning information about the structure of
0641: * the plot (<code>null</code> permitted).
0642: *
0643: * @return The renderer state.
0644: */
0645: public CategoryItemRendererState initialise(Graphics2D g2,
0646: Rectangle2D dataArea, CategoryPlot plot, int rendererIndex,
0647: PlotRenderingInfo info) {
0648:
0649: setPlot(plot);
0650: CategoryDataset data = plot.getDataset(rendererIndex);
0651: if (data != null) {
0652: this .rowCount = data.getRowCount();
0653: this .columnCount = data.getColumnCount();
0654: } else {
0655: this .rowCount = 0;
0656: this .columnCount = 0;
0657: }
0658: return createState(info);
0659:
0660: }
0661:
0662: /**
0663: * Returns the range of values the renderer requires to display all the
0664: * items from the specified dataset.
0665: *
0666: * @param dataset the dataset (<code>null</code> permitted).
0667: *
0668: * @return The range (or <code>null</code> if the dataset is
0669: * <code>null</code> or empty).
0670: */
0671: public Range findRangeBounds(CategoryDataset dataset) {
0672: return DatasetUtilities.findRangeBounds(dataset);
0673: }
0674:
0675: /**
0676: * Draws a background for the data area. The default implementation just
0677: * gets the plot to draw the background, but some renderers will override
0678: * this behaviour.
0679: *
0680: * @param g2 the graphics device.
0681: * @param plot the plot.
0682: * @param dataArea the data area.
0683: */
0684: public void drawBackground(Graphics2D g2, CategoryPlot plot,
0685: Rectangle2D dataArea) {
0686:
0687: plot.drawBackground(g2, dataArea);
0688:
0689: }
0690:
0691: /**
0692: * Draws an outline for the data area. The default implementation just
0693: * gets the plot to draw the outline, but some renderers will override this
0694: * behaviour.
0695: *
0696: * @param g2 the graphics device.
0697: * @param plot the plot.
0698: * @param dataArea the data area.
0699: */
0700: public void drawOutline(Graphics2D g2, CategoryPlot plot,
0701: Rectangle2D dataArea) {
0702:
0703: plot.drawOutline(g2, dataArea);
0704:
0705: }
0706:
0707: /**
0708: * Draws a grid line against the domain axis.
0709: * <P>
0710: * Note that this default implementation assumes that the horizontal axis
0711: * is the domain axis. If this is not the case, you will need to override
0712: * this method.
0713: *
0714: * @param g2 the graphics device.
0715: * @param plot the plot.
0716: * @param dataArea the area for plotting data (not yet adjusted for any
0717: * 3D effect).
0718: * @param value the Java2D value at which the grid line should be drawn.
0719: *
0720: * @see #drawRangeGridline(Graphics2D, CategoryPlot, ValueAxis,
0721: * Rectangle2D, double)
0722: */
0723: public void drawDomainGridline(Graphics2D g2, CategoryPlot plot,
0724: Rectangle2D dataArea, double value) {
0725:
0726: Line2D line = null;
0727: PlotOrientation orientation = plot.getOrientation();
0728:
0729: if (orientation == PlotOrientation.HORIZONTAL) {
0730: line = new Line2D.Double(dataArea.getMinX(), value,
0731: dataArea.getMaxX(), value);
0732: } else if (orientation == PlotOrientation.VERTICAL) {
0733: line = new Line2D.Double(value, dataArea.getMinY(), value,
0734: dataArea.getMaxY());
0735: }
0736:
0737: Paint paint = plot.getDomainGridlinePaint();
0738: if (paint == null) {
0739: paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT;
0740: }
0741: g2.setPaint(paint);
0742:
0743: Stroke stroke = plot.getDomainGridlineStroke();
0744: if (stroke == null) {
0745: stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE;
0746: }
0747: g2.setStroke(stroke);
0748:
0749: g2.draw(line);
0750:
0751: }
0752:
0753: /**
0754: * Draws a grid line against the range axis.
0755: *
0756: * @param g2 the graphics device.
0757: * @param plot the plot.
0758: * @param axis the value axis.
0759: * @param dataArea the area for plotting data (not yet adjusted for any
0760: * 3D effect).
0761: * @param value the value at which the grid line should be drawn.
0762: *
0763: * @see #drawDomainGridline(Graphics2D, CategoryPlot, Rectangle2D, double)
0764: *
0765: */
0766: public void drawRangeGridline(Graphics2D g2, CategoryPlot plot,
0767: ValueAxis axis, Rectangle2D dataArea, double value) {
0768:
0769: Range range = axis.getRange();
0770: if (!range.contains(value)) {
0771: return;
0772: }
0773:
0774: PlotOrientation orientation = plot.getOrientation();
0775: double v = axis.valueToJava2D(value, dataArea, plot
0776: .getRangeAxisEdge());
0777: Line2D line = null;
0778: if (orientation == PlotOrientation.HORIZONTAL) {
0779: line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea
0780: .getMaxY());
0781: } else if (orientation == PlotOrientation.VERTICAL) {
0782: line = new Line2D.Double(dataArea.getMinX(), v, dataArea
0783: .getMaxX(), v);
0784: }
0785:
0786: Paint paint = plot.getRangeGridlinePaint();
0787: if (paint == null) {
0788: paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT;
0789: }
0790: g2.setPaint(paint);
0791:
0792: Stroke stroke = plot.getRangeGridlineStroke();
0793: if (stroke == null) {
0794: stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE;
0795: }
0796: g2.setStroke(stroke);
0797:
0798: g2.draw(line);
0799:
0800: }
0801:
0802: /**
0803: * Draws a marker for the domain axis.
0804: *
0805: * @param g2 the graphics device (not <code>null</code>).
0806: * @param plot the plot (not <code>null</code>).
0807: * @param axis the range axis (not <code>null</code>).
0808: * @param marker the marker to be drawn (not <code>null</code>).
0809: * @param dataArea the area inside the axes (not <code>null</code>).
0810: *
0811: * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker,
0812: * Rectangle2D)
0813: */
0814: public void drawDomainMarker(Graphics2D g2, CategoryPlot plot,
0815: CategoryAxis axis, CategoryMarker marker,
0816: Rectangle2D dataArea) {
0817:
0818: Comparable category = marker.getKey();
0819: CategoryDataset dataset = plot
0820: .getDataset(plot.getIndexOf(this ));
0821: int columnIndex = dataset.getColumnIndex(category);
0822: if (columnIndex < 0) {
0823: return;
0824: }
0825:
0826: final Composite savedComposite = g2.getComposite();
0827: g2.setComposite(AlphaComposite.getInstance(
0828: AlphaComposite.SRC_OVER, marker.getAlpha()));
0829:
0830: PlotOrientation orientation = plot.getOrientation();
0831: Rectangle2D bounds = null;
0832: if (marker.getDrawAsLine()) {
0833: double v = axis.getCategoryMiddle(columnIndex, dataset
0834: .getColumnCount(), dataArea, plot
0835: .getDomainAxisEdge());
0836: Line2D line = null;
0837: if (orientation == PlotOrientation.HORIZONTAL) {
0838: line = new Line2D.Double(dataArea.getMinX(), v,
0839: dataArea.getMaxX(), v);
0840: } else if (orientation == PlotOrientation.VERTICAL) {
0841: line = new Line2D.Double(v, dataArea.getMinY(), v,
0842: dataArea.getMaxY());
0843: }
0844: g2.setPaint(marker.getPaint());
0845: g2.setStroke(marker.getStroke());
0846: g2.draw(line);
0847: bounds = line.getBounds2D();
0848: } else {
0849: double v0 = axis.getCategoryStart(columnIndex, dataset
0850: .getColumnCount(), dataArea, plot
0851: .getDomainAxisEdge());
0852: double v1 = axis.getCategoryEnd(columnIndex, dataset
0853: .getColumnCount(), dataArea, plot
0854: .getDomainAxisEdge());
0855: Rectangle2D area = null;
0856: if (orientation == PlotOrientation.HORIZONTAL) {
0857: area = new Rectangle2D.Double(dataArea.getMinX(), v0,
0858: dataArea.getWidth(), (v1 - v0));
0859: } else if (orientation == PlotOrientation.VERTICAL) {
0860: area = new Rectangle2D.Double(v0, dataArea.getMinY(),
0861: (v1 - v0), dataArea.getHeight());
0862: }
0863: g2.setPaint(marker.getPaint());
0864: g2.fill(area);
0865: bounds = area;
0866: }
0867:
0868: String label = marker.getLabel();
0869: RectangleAnchor anchor = marker.getLabelAnchor();
0870: if (label != null) {
0871: Font labelFont = marker.getLabelFont();
0872: g2.setFont(labelFont);
0873: g2.setPaint(marker.getLabelPaint());
0874: Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
0875: g2, orientation, dataArea, bounds, marker
0876: .getLabelOffset(), marker
0877: .getLabelOffsetType(), anchor);
0878: TextUtilities.drawAlignedString(label, g2,
0879: (float) coordinates.getX(), (float) coordinates
0880: .getY(), marker.getLabelTextAnchor());
0881: }
0882: g2.setComposite(savedComposite);
0883: }
0884:
0885: /**
0886: * Draws a marker for the range axis.
0887: *
0888: * @param g2 the graphics device (not <code>null</code>).
0889: * @param plot the plot (not <code>null</code>).
0890: * @param axis the range axis (not <code>null</code>).
0891: * @param marker the marker to be drawn (not <code>null</code>).
0892: * @param dataArea the area inside the axes (not <code>null</code>).
0893: *
0894: * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis,
0895: * CategoryMarker, Rectangle2D)
0896: */
0897: public void drawRangeMarker(Graphics2D g2, CategoryPlot plot,
0898: ValueAxis axis, Marker marker, Rectangle2D dataArea) {
0899:
0900: if (marker instanceof ValueMarker) {
0901: ValueMarker vm = (ValueMarker) marker;
0902: double value = vm.getValue();
0903: Range range = axis.getRange();
0904:
0905: if (!range.contains(value)) {
0906: return;
0907: }
0908:
0909: final Composite savedComposite = g2.getComposite();
0910: g2.setComposite(AlphaComposite.getInstance(
0911: AlphaComposite.SRC_OVER, marker.getAlpha()));
0912:
0913: PlotOrientation orientation = plot.getOrientation();
0914: double v = axis.valueToJava2D(value, dataArea, plot
0915: .getRangeAxisEdge());
0916: Line2D line = null;
0917: if (orientation == PlotOrientation.HORIZONTAL) {
0918: line = new Line2D.Double(v, dataArea.getMinY(), v,
0919: dataArea.getMaxY());
0920: } else if (orientation == PlotOrientation.VERTICAL) {
0921: line = new Line2D.Double(dataArea.getMinX(), v,
0922: dataArea.getMaxX(), v);
0923: }
0924:
0925: g2.setPaint(marker.getPaint());
0926: g2.setStroke(marker.getStroke());
0927: g2.draw(line);
0928:
0929: String label = marker.getLabel();
0930: RectangleAnchor anchor = marker.getLabelAnchor();
0931: if (label != null) {
0932: Font labelFont = marker.getLabelFont();
0933: g2.setFont(labelFont);
0934: g2.setPaint(marker.getLabelPaint());
0935: Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
0936: g2, orientation, dataArea, line.getBounds2D(),
0937: marker.getLabelOffset(),
0938: LengthAdjustmentType.EXPAND, anchor);
0939: TextUtilities.drawAlignedString(label, g2,
0940: (float) coordinates.getX(), (float) coordinates
0941: .getY(), marker.getLabelTextAnchor());
0942: }
0943: g2.setComposite(savedComposite);
0944: } else if (marker instanceof IntervalMarker) {
0945: IntervalMarker im = (IntervalMarker) marker;
0946: double start = im.getStartValue();
0947: double end = im.getEndValue();
0948: Range range = axis.getRange();
0949: if (!(range.intersects(start, end))) {
0950: return;
0951: }
0952:
0953: final Composite savedComposite = g2.getComposite();
0954: g2.setComposite(AlphaComposite.getInstance(
0955: AlphaComposite.SRC_OVER, marker.getAlpha()));
0956:
0957: double start2d = axis.valueToJava2D(start, dataArea, plot
0958: .getRangeAxisEdge());
0959: double end2d = axis.valueToJava2D(end, dataArea, plot
0960: .getRangeAxisEdge());
0961: double low = Math.min(start2d, end2d);
0962: double high = Math.max(start2d, end2d);
0963:
0964: PlotOrientation orientation = plot.getOrientation();
0965: Rectangle2D rect = null;
0966: if (orientation == PlotOrientation.HORIZONTAL) {
0967: // clip left and right bounds to data area
0968: low = Math.max(low, dataArea.getMinX());
0969: high = Math.min(high, dataArea.getMaxX());
0970: rect = new Rectangle2D.Double(low, dataArea.getMinY(),
0971: high - low, dataArea.getHeight());
0972: } else if (orientation == PlotOrientation.VERTICAL) {
0973: // clip top and bottom bounds to data area
0974: low = Math.max(low, dataArea.getMinY());
0975: high = Math.min(high, dataArea.getMaxY());
0976: rect = new Rectangle2D.Double(dataArea.getMinX(), low,
0977: dataArea.getWidth(), high - low);
0978: }
0979: Paint p = marker.getPaint();
0980: if (p instanceof GradientPaint) {
0981: GradientPaint gp = (GradientPaint) p;
0982: GradientPaintTransformer t = im
0983: .getGradientPaintTransformer();
0984: if (t != null) {
0985: gp = t.transform(gp, rect);
0986: }
0987: g2.setPaint(gp);
0988: } else {
0989: g2.setPaint(p);
0990: }
0991: g2.fill(rect);
0992:
0993: // now draw the outlines, if visible...
0994: if (im.getOutlinePaint() != null
0995: && im.getOutlineStroke() != null) {
0996: if (orientation == PlotOrientation.VERTICAL) {
0997: Line2D line = new Line2D.Double();
0998: double x0 = dataArea.getMinX();
0999: double x1 = dataArea.getMaxX();
1000: g2.setPaint(im.getOutlinePaint());
1001: g2.setStroke(im.getOutlineStroke());
1002: if (range.contains(start)) {
1003: line.setLine(x0, start2d, x1, start2d);
1004: g2.draw(line);
1005: }
1006: if (range.contains(end)) {
1007: line.setLine(x0, end2d, x1, end2d);
1008: g2.draw(line);
1009: }
1010: } else { // PlotOrientation.HORIZONTAL
1011: Line2D line = new Line2D.Double();
1012: double y0 = dataArea.getMinY();
1013: double y1 = dataArea.getMaxY();
1014: g2.setPaint(im.getOutlinePaint());
1015: g2.setStroke(im.getOutlineStroke());
1016: if (range.contains(start)) {
1017: line.setLine(start2d, y0, start2d, y1);
1018: g2.draw(line);
1019: }
1020: if (range.contains(end)) {
1021: line.setLine(end2d, y0, end2d, y1);
1022: g2.draw(line);
1023: }
1024: }
1025: }
1026:
1027: String label = marker.getLabel();
1028: RectangleAnchor anchor = marker.getLabelAnchor();
1029: if (label != null) {
1030: Font labelFont = marker.getLabelFont();
1031: g2.setFont(labelFont);
1032: g2.setPaint(marker.getLabelPaint());
1033: Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1034: g2, orientation, dataArea, rect, marker
1035: .getLabelOffset(), marker
1036: .getLabelOffsetType(), anchor);
1037: TextUtilities.drawAlignedString(label, g2,
1038: (float) coordinates.getX(), (float) coordinates
1039: .getY(), marker.getLabelTextAnchor());
1040: }
1041: g2.setComposite(savedComposite);
1042: }
1043: }
1044:
1045: /**
1046: * Calculates the (x, y) coordinates for drawing the label for a marker on
1047: * the range axis.
1048: *
1049: * @param g2 the graphics device.
1050: * @param orientation the plot orientation.
1051: * @param dataArea the data area.
1052: * @param markerArea the rectangle surrounding the marker.
1053: * @param markerOffset the marker offset.
1054: * @param labelOffsetType the label offset type.
1055: * @param anchor the label anchor.
1056: *
1057: * @return The coordinates for drawing the marker label.
1058: */
1059: protected Point2D calculateDomainMarkerTextAnchorPoint(
1060: Graphics2D g2, PlotOrientation orientation,
1061: Rectangle2D dataArea, Rectangle2D markerArea,
1062: RectangleInsets markerOffset,
1063: LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) {
1064:
1065: Rectangle2D anchorRect = null;
1066: if (orientation == PlotOrientation.HORIZONTAL) {
1067: anchorRect = markerOffset.createAdjustedRectangle(
1068: markerArea, LengthAdjustmentType.CONTRACT,
1069: labelOffsetType);
1070: } else if (orientation == PlotOrientation.VERTICAL) {
1071: anchorRect = markerOffset.createAdjustedRectangle(
1072: markerArea, labelOffsetType,
1073: LengthAdjustmentType.CONTRACT);
1074: }
1075: return RectangleAnchor.coordinates(anchorRect, anchor);
1076:
1077: }
1078:
1079: /**
1080: * Calculates the (x, y) coordinates for drawing a marker label.
1081: *
1082: * @param g2 the graphics device.
1083: * @param orientation the plot orientation.
1084: * @param dataArea the data area.
1085: * @param markerArea the rectangle surrounding the marker.
1086: * @param markerOffset the marker offset.
1087: * @param labelOffsetType the label offset type.
1088: * @param anchor the label anchor.
1089: *
1090: * @return The coordinates for drawing the marker label.
1091: */
1092: protected Point2D calculateRangeMarkerTextAnchorPoint(
1093: Graphics2D g2, PlotOrientation orientation,
1094: Rectangle2D dataArea, Rectangle2D markerArea,
1095: RectangleInsets markerOffset,
1096: LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) {
1097:
1098: Rectangle2D anchorRect = null;
1099: if (orientation == PlotOrientation.HORIZONTAL) {
1100: anchorRect = markerOffset.createAdjustedRectangle(
1101: markerArea, labelOffsetType,
1102: LengthAdjustmentType.CONTRACT);
1103: } else if (orientation == PlotOrientation.VERTICAL) {
1104: anchorRect = markerOffset.createAdjustedRectangle(
1105: markerArea, LengthAdjustmentType.CONTRACT,
1106: labelOffsetType);
1107: }
1108: return RectangleAnchor.coordinates(anchorRect, anchor);
1109:
1110: }
1111:
1112: /**
1113: * Returns a legend item for a series. This default implementation will
1114: * return <code>null</code> if {@link #isSeriesVisible(int)} or
1115: * {@link #isSeriesVisibleInLegend(int)} returns <code>false</code>.
1116: *
1117: * @param datasetIndex the dataset index (zero-based).
1118: * @param series the series index (zero-based).
1119: *
1120: * @return The legend item (possibly <code>null</code>).
1121: *
1122: * @see #getLegendItems()
1123: */
1124: public LegendItem getLegendItem(int datasetIndex, int series) {
1125:
1126: CategoryPlot p = getPlot();
1127: if (p == null) {
1128: return null;
1129: }
1130:
1131: // check that a legend item needs to be displayed...
1132: if (!isSeriesVisible(series)
1133: || !isSeriesVisibleInLegend(series)) {
1134: return null;
1135: }
1136:
1137: CategoryDataset dataset = p.getDataset(datasetIndex);
1138: String label = this .legendItemLabelGenerator.generateLabel(
1139: dataset, series);
1140: String description = label;
1141: String toolTipText = null;
1142: if (this .legendItemToolTipGenerator != null) {
1143: toolTipText = this .legendItemToolTipGenerator
1144: .generateLabel(dataset, series);
1145: }
1146: String urlText = null;
1147: if (this .legendItemURLGenerator != null) {
1148: urlText = this .legendItemURLGenerator.generateLabel(
1149: dataset, series);
1150: }
1151: Shape shape = lookupSeriesShape(series);
1152: Paint paint = lookupSeriesPaint(series);
1153: Paint outlinePaint = lookupSeriesOutlinePaint(series);
1154: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
1155:
1156: LegendItem item = new LegendItem(label, description,
1157: toolTipText, urlText, shape, paint, outlineStroke,
1158: outlinePaint);
1159: item.setSeriesKey(dataset.getRowKey(series));
1160: item.setSeriesIndex(series);
1161: item.setDataset(dataset);
1162: item.setDatasetIndex(datasetIndex);
1163: return item;
1164: }
1165:
1166: /**
1167: * Tests this renderer for equality with another object.
1168: *
1169: * @param obj the object.
1170: *
1171: * @return <code>true</code> or <code>false</code>.
1172: */
1173: public boolean equals(Object obj) {
1174:
1175: if (obj == this ) {
1176: return true;
1177: }
1178: if (!(obj instanceof AbstractCategoryItemRenderer)) {
1179: return false;
1180: }
1181: AbstractCategoryItemRenderer that = (AbstractCategoryItemRenderer) obj;
1182:
1183: if (!ObjectUtilities.equal(this .itemLabelGenerator,
1184: that.itemLabelGenerator)) {
1185: return false;
1186: }
1187: if (!ObjectUtilities.equal(this .itemLabelGeneratorList,
1188: that.itemLabelGeneratorList)) {
1189: return false;
1190: }
1191: if (!ObjectUtilities.equal(this .baseItemLabelGenerator,
1192: that.baseItemLabelGenerator)) {
1193: return false;
1194: }
1195: if (!ObjectUtilities.equal(this .toolTipGenerator,
1196: that.toolTipGenerator)) {
1197: return false;
1198: }
1199: if (!ObjectUtilities.equal(this .toolTipGeneratorList,
1200: that.toolTipGeneratorList)) {
1201: return false;
1202: }
1203: if (!ObjectUtilities.equal(this .baseToolTipGenerator,
1204: that.baseToolTipGenerator)) {
1205: return false;
1206: }
1207: if (!ObjectUtilities.equal(this .itemURLGenerator,
1208: that.itemURLGenerator)) {
1209: return false;
1210: }
1211: if (!ObjectUtilities.equal(this .itemURLGeneratorList,
1212: that.itemURLGeneratorList)) {
1213: return false;
1214: }
1215: if (!ObjectUtilities.equal(this .baseItemURLGenerator,
1216: that.baseItemURLGenerator)) {
1217: return false;
1218: }
1219: if (!ObjectUtilities.equal(this .legendItemLabelGenerator,
1220: that.legendItemLabelGenerator)) {
1221: return false;
1222: }
1223: if (!ObjectUtilities.equal(this .legendItemToolTipGenerator,
1224: that.legendItemToolTipGenerator)) {
1225: return false;
1226: }
1227: if (!ObjectUtilities.equal(this .legendItemURLGenerator,
1228: that.legendItemURLGenerator)) {
1229: return false;
1230: }
1231: return super .equals(obj);
1232: }
1233:
1234: /**
1235: * Returns a hash code for the renderer.
1236: *
1237: * @return The hash code.
1238: */
1239: public int hashCode() {
1240: int result = super .hashCode();
1241: return result;
1242: }
1243:
1244: /**
1245: * Returns the drawing supplier from the plot.
1246: *
1247: * @return The drawing supplier (possibly <code>null</code>).
1248: */
1249: public DrawingSupplier getDrawingSupplier() {
1250: DrawingSupplier result = null;
1251: CategoryPlot cp = getPlot();
1252: if (cp != null) {
1253: result = cp.getDrawingSupplier();
1254: }
1255: return result;
1256: }
1257:
1258: /**
1259: * Draws an item label.
1260: *
1261: * @param g2 the graphics device.
1262: * @param orientation the orientation.
1263: * @param dataset the dataset.
1264: * @param row the row.
1265: * @param column the column.
1266: * @param x the x coordinate (in Java2D space).
1267: * @param y the y coordinate (in Java2D space).
1268: * @param negative indicates a negative value (which affects the item
1269: * label position).
1270: */
1271: protected void drawItemLabel(Graphics2D g2,
1272: PlotOrientation orientation, CategoryDataset dataset,
1273: int row, int column, double x, double y, boolean negative) {
1274:
1275: CategoryItemLabelGenerator generator = getItemLabelGenerator(
1276: row, column);
1277: if (generator != null) {
1278: Font labelFont = getItemLabelFont(row, column);
1279: Paint paint = getItemLabelPaint(row, column);
1280: g2.setFont(labelFont);
1281: g2.setPaint(paint);
1282: String label = generator
1283: .generateLabel(dataset, row, column);
1284: ItemLabelPosition position = null;
1285: if (!negative) {
1286: position = getPositiveItemLabelPosition(row, column);
1287: } else {
1288: position = getNegativeItemLabelPosition(row, column);
1289: }
1290: Point2D anchorPoint = calculateLabelAnchorPoint(position
1291: .getItemLabelAnchor(), x, y, orientation);
1292: TextUtilities.drawRotatedString(label, g2,
1293: (float) anchorPoint.getX(), (float) anchorPoint
1294: .getY(), position.getTextAnchor(), position
1295: .getAngle(), position.getRotationAnchor());
1296: }
1297:
1298: }
1299:
1300: /**
1301: * Returns an independent copy of the renderer. The <code>plot</code>
1302: * reference is shallow copied.
1303: *
1304: * @return A clone.
1305: *
1306: * @throws CloneNotSupportedException can be thrown if one of the objects
1307: * belonging to the renderer does not support cloning (for example,
1308: * an item label generator).
1309: */
1310: public Object clone() throws CloneNotSupportedException {
1311:
1312: AbstractCategoryItemRenderer clone = (AbstractCategoryItemRenderer) super
1313: .clone();
1314:
1315: if (this .itemLabelGenerator != null) {
1316: if (this .itemLabelGenerator instanceof PublicCloneable) {
1317: PublicCloneable pc = (PublicCloneable) this .itemLabelGenerator;
1318: clone.itemLabelGenerator = (CategoryItemLabelGenerator) pc
1319: .clone();
1320: } else {
1321: throw new CloneNotSupportedException(
1322: "ItemLabelGenerator not cloneable.");
1323: }
1324: }
1325:
1326: if (this .itemLabelGeneratorList != null) {
1327: clone.itemLabelGeneratorList = (ObjectList) this .itemLabelGeneratorList
1328: .clone();
1329: }
1330:
1331: if (this .baseItemLabelGenerator != null) {
1332: if (this .baseItemLabelGenerator instanceof PublicCloneable) {
1333: PublicCloneable pc = (PublicCloneable) this .baseItemLabelGenerator;
1334: clone.baseItemLabelGenerator = (CategoryItemLabelGenerator) pc
1335: .clone();
1336: } else {
1337: throw new CloneNotSupportedException(
1338: "ItemLabelGenerator not cloneable.");
1339: }
1340: }
1341:
1342: if (this .toolTipGenerator != null) {
1343: if (this .toolTipGenerator instanceof PublicCloneable) {
1344: PublicCloneable pc = (PublicCloneable) this .toolTipGenerator;
1345: clone.toolTipGenerator = (CategoryToolTipGenerator) pc
1346: .clone();
1347: } else {
1348: throw new CloneNotSupportedException(
1349: "Tool tip generator not cloneable.");
1350: }
1351: }
1352:
1353: if (this .toolTipGeneratorList != null) {
1354: clone.toolTipGeneratorList = (ObjectList) this .toolTipGeneratorList
1355: .clone();
1356: }
1357:
1358: if (this .baseToolTipGenerator != null) {
1359: if (this .baseToolTipGenerator instanceof PublicCloneable) {
1360: PublicCloneable pc = (PublicCloneable) this .baseToolTipGenerator;
1361: clone.baseToolTipGenerator = (CategoryToolTipGenerator) pc
1362: .clone();
1363: } else {
1364: throw new CloneNotSupportedException(
1365: "Base tool tip generator not cloneable.");
1366: }
1367: }
1368:
1369: if (this .itemURLGenerator != null) {
1370: if (this .itemURLGenerator instanceof PublicCloneable) {
1371: PublicCloneable pc = (PublicCloneable) this .itemURLGenerator;
1372: clone.itemURLGenerator = (CategoryURLGenerator) pc
1373: .clone();
1374: } else {
1375: throw new CloneNotSupportedException(
1376: "Item URL generator not cloneable.");
1377: }
1378: }
1379:
1380: if (this .itemURLGeneratorList != null) {
1381: clone.itemURLGeneratorList = (ObjectList) this .itemURLGeneratorList
1382: .clone();
1383: }
1384:
1385: if (this .baseItemURLGenerator != null) {
1386: if (this .baseItemURLGenerator instanceof PublicCloneable) {
1387: PublicCloneable pc = (PublicCloneable) this .baseItemURLGenerator;
1388: clone.baseItemURLGenerator = (CategoryURLGenerator) pc
1389: .clone();
1390: } else {
1391: throw new CloneNotSupportedException(
1392: "Base item URL generator not cloneable.");
1393: }
1394: }
1395:
1396: if (this .legendItemLabelGenerator instanceof PublicCloneable) {
1397: clone.legendItemLabelGenerator = (CategorySeriesLabelGenerator) ObjectUtilities
1398: .clone(this .legendItemLabelGenerator);
1399: }
1400: if (this .legendItemToolTipGenerator instanceof PublicCloneable) {
1401: clone.legendItemToolTipGenerator = (CategorySeriesLabelGenerator) ObjectUtilities
1402: .clone(this .legendItemToolTipGenerator);
1403: }
1404: if (this .legendItemURLGenerator instanceof PublicCloneable) {
1405: clone.legendItemURLGenerator = (CategorySeriesLabelGenerator) ObjectUtilities
1406: .clone(this .legendItemURLGenerator);
1407: }
1408: return clone;
1409: }
1410:
1411: /**
1412: * Returns a domain axis for a plot.
1413: *
1414: * @param plot the plot.
1415: * @param index the axis index.
1416: *
1417: * @return A domain axis.
1418: */
1419: protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) {
1420: CategoryAxis result = plot.getDomainAxis(index);
1421: if (result == null) {
1422: result = plot.getDomainAxis();
1423: }
1424: return result;
1425: }
1426:
1427: /**
1428: * Returns a range axis for a plot.
1429: *
1430: * @param plot the plot.
1431: * @param index the axis index.
1432: *
1433: * @return A range axis.
1434: */
1435: protected ValueAxis getRangeAxis(CategoryPlot plot, int index) {
1436: ValueAxis result = plot.getRangeAxis(index);
1437: if (result == null) {
1438: result = plot.getRangeAxis();
1439: }
1440: return result;
1441: }
1442:
1443: /**
1444: * Returns a (possibly empty) collection of legend items for the series
1445: * that this renderer is responsible for drawing.
1446: *
1447: * @return The legend item collection (never <code>null</code>).
1448: *
1449: * @see #getLegendItem(int, int)
1450: */
1451: public LegendItemCollection getLegendItems() {
1452: if (this .plot == null) {
1453: return new LegendItemCollection();
1454: }
1455: LegendItemCollection result = new LegendItemCollection();
1456: int index = this .plot.getIndexOf(this );
1457: CategoryDataset dataset = this .plot.getDataset(index);
1458: if (dataset != null) {
1459: int seriesCount = dataset.getRowCount();
1460: for (int i = 0; i < seriesCount; i++) {
1461: if (isSeriesVisibleInLegend(i)) {
1462: LegendItem item = getLegendItem(index, i);
1463: if (item != null) {
1464: result.add(item);
1465: }
1466: }
1467: }
1468:
1469: }
1470: return result;
1471: }
1472:
1473: /**
1474: * Returns the legend item label generator.
1475: *
1476: * @return The label generator (never <code>null</code>).
1477: *
1478: * @see #setLegendItemLabelGenerator(CategorySeriesLabelGenerator)
1479: */
1480: public CategorySeriesLabelGenerator getLegendItemLabelGenerator() {
1481: return this .legendItemLabelGenerator;
1482: }
1483:
1484: /**
1485: * Sets the legend item label generator and sends a
1486: * {@link RendererChangeEvent} to all registered listeners.
1487: *
1488: * @param generator the generator (<code>null</code> not permitted).
1489: *
1490: * @see #getLegendItemLabelGenerator()
1491: */
1492: public void setLegendItemLabelGenerator(
1493: CategorySeriesLabelGenerator generator) {
1494: if (generator == null) {
1495: throw new IllegalArgumentException(
1496: "Null 'generator' argument.");
1497: }
1498: this .legendItemLabelGenerator = generator;
1499: notifyListeners(new RendererChangeEvent(this ));
1500: }
1501:
1502: /**
1503: * Returns the legend item tool tip generator.
1504: *
1505: * @return The tool tip generator (possibly <code>null</code>).
1506: *
1507: * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator)
1508: */
1509: public CategorySeriesLabelGenerator getLegendItemToolTipGenerator() {
1510: return this .legendItemToolTipGenerator;
1511: }
1512:
1513: /**
1514: * Sets the legend item tool tip generator and sends a
1515: * {@link RendererChangeEvent} to all registered listeners.
1516: *
1517: * @param generator the generator (<code>null</code> permitted).
1518: *
1519: * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator)
1520: */
1521: public void setLegendItemToolTipGenerator(
1522: CategorySeriesLabelGenerator generator) {
1523: this .legendItemToolTipGenerator = generator;
1524: notifyListeners(new RendererChangeEvent(this ));
1525: }
1526:
1527: /**
1528: * Returns the legend item URL generator.
1529: *
1530: * @return The URL generator (possibly <code>null</code>).
1531: *
1532: * @see #setLegendItemURLGenerator(CategorySeriesLabelGenerator)
1533: */
1534: public CategorySeriesLabelGenerator getLegendItemURLGenerator() {
1535: return this .legendItemURLGenerator;
1536: }
1537:
1538: /**
1539: * Sets the legend item URL generator and sends a
1540: * {@link RendererChangeEvent} to all registered listeners.
1541: *
1542: * @param generator the generator (<code>null</code> permitted).
1543: *
1544: * @see #getLegendItemURLGenerator()
1545: */
1546: public void setLegendItemURLGenerator(
1547: CategorySeriesLabelGenerator generator) {
1548: this .legendItemURLGenerator = generator;
1549: notifyListeners(new RendererChangeEvent(this ));
1550: }
1551:
1552: /**
1553: * Adds an entity with the specified hotspot.
1554: *
1555: * @param entities the entity collection.
1556: * @param dataset the dataset.
1557: * @param row the row index.
1558: * @param column the column index.
1559: * @param hotspot the hotspot.
1560: */
1561: protected void addItemEntity(EntityCollection entities,
1562: CategoryDataset dataset, int row, int column, Shape hotspot) {
1563:
1564: String tip = null;
1565: CategoryToolTipGenerator tipster = getToolTipGenerator(row,
1566: column);
1567: if (tipster != null) {
1568: tip = tipster.generateToolTip(dataset, row, column);
1569: }
1570: String url = null;
1571: CategoryURLGenerator urlster = getItemURLGenerator(row, column);
1572: if (urlster != null) {
1573: url = urlster.generateURL(dataset, row, column);
1574: }
1575: CategoryItemEntity entity = new CategoryItemEntity(hotspot,
1576: tip, url, dataset, dataset.getRowKey(row), dataset
1577: .getColumnKey(column));
1578: entities.add(entity);
1579:
1580: }
1581:
1582: }
|