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: * CategoryPlot.java
0029: * -----------------
0030: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Jeremy Bowman;
0034: * Arnaud Lelievre;
0035: *
0036: * $Id: CategoryPlot.java,v 1.23.2.17 2007/06/07 12:49:36 mungady Exp $
0037: *
0038: * Changes (from 21-Jun-2001)
0039: * --------------------------
0040: * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
0041: * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG);
0042: * 18-Sep-2001 : Updated header (DG);
0043: * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
0044: * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
0045: * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of
0046: * available space rather than a fixed number of units (DG);
0047: * 12-Dec-2001 : Changed constructors to protected (DG);
0048: * 13-Dec-2001 : Added tooltips (DG);
0049: * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added
0050: * some argument checking code. Thanks to Taoufik Romdhane for
0051: * suggesting this (DG);
0052: * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated
0053: * alpha-transparency for Plot and subclasses (DG);
0054: * 06-Mar-2002 : Updated import statements (DG);
0055: * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code
0056: * to use the CategoryItemRenderer interface (DG);
0057: * 22-Mar-2002 : Dropped the getCategories() method (DG);
0058: * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot
0059: * class (DG);
0060: * 29-Apr-2002 : New methods to support printing values at the end of bars,
0061: * contributed by Jeremy Bowman (DG);
0062: * 11-May-2002 : New methods for label visibility and overlaid plot support,
0063: * contributed by Jeremy Bowman (DG);
0064: * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the
0065: * renderer. Moved constants into the CategoryPlotConstants
0066: * interface. Updated Javadoc comments (DG);
0067: * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and
0068: * lower bound on the range axis (if necessary), updated
0069: * Javadocs (DG);
0070: * 25-Jun-2002 : Removed redundant imports (DG);
0071: * 20-Aug-2002 : Changed the constructor for Marker (DG);
0072: * 28-Aug-2002 : Added listener notification to setDomainAxis() and
0073: * setRangeAxis() (DG);
0074: * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by
0075: * Checkstyle (DG);
0076: * 28-Oct-2002 : Changes to the CategoryDataset interface (DG);
0077: * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
0078: * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG);
0079: * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
0080: * these were set in the axes) (DG);
0081: * 19-Nov-2002 : Added axis location parameters to constructor (DG);
0082: * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG);
0083: * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG);
0084: * 26-Mar-2003 : Implemented Serializable (DG);
0085: * 02-May-2003 : Moved render() method up from subclasses. Added secondary
0086: * range markers. Added an attribute to control the dataset
0087: * rendering order. Added a drawAnnotations() method. Changed
0088: * the axis location from an int to an AxisLocation (DG);
0089: * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into
0090: * this class (DG);
0091: * 02-Jun-2003 : Removed check for range axis compatibility (DG);
0092: * 04-Jul-2003 : Added a domain gridline position attribute (DG);
0093: * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG);
0094: * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG);
0095: * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset
0096: * changes) (DG);
0097: * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and
0098: * 790407 (initialise method) (DG);
0099: * 08-Sep-2003 : Added internationalization via use of properties
0100: * resourceBundle (RFE 690236) (AL);
0101: * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed
0102: * ValueAxis API (DG);
0103: * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG);
0104: * 15-Sep-2003 : Fixed two bugs in serialization, implemented
0105: * PublicCloneable (DG);
0106: * 23-Oct-2003 : Added event notification for changes to renderer (DG);
0107: * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG);
0108: * 03-Dec-2003 : Modified draw method to accept anchor (DG);
0109: * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
0110: * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is
0111: * stacked (DG);
0112: * 12-May-2004 : Added fixed legend items (DG);
0113: * 19-May-2004 : Added check for null legend item from renderer (DG);
0114: * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG);
0115: * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis()
0116: * --> datasetsMappedToRangeAxis(), and ensured that returned
0117: * list doesn't contain null datasets (DG);
0118: * 12-Nov-2004 : Implemented new Zoomable interface (DG);
0119: * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in
0120: * CategoryItemRenderer (DG);
0121: * 04-May-2005 : Fixed serialization of range markers (DG);
0122: * 05-May-2005 : Updated draw() method parameters (DG);
0123: * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
0124: * RFE 1183100 (DG);
0125: * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
0126: * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
0127: * 02-Jun-2005 : Added support for domain markers (DG);
0128: * 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG);
0129: * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
0130: * 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to
0131: * match XYPlot (see RFE 1220495) (DG);
0132: * ------------- JFREECHART 1.0.x ---------------------------------------------
0133: * 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the
0134: * renderer might influence the axis range (DG);
0135: * 27-Jan-2006 : Added various null argument checks (DG);
0136: * 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing
0137: * category labels, thanks to Adriaan Joubert (1277726) (DG);
0138: * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
0139: * 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and
0140: * getCategoriesForAxis() methods (DG);
0141: * 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and
0142: * setRowRenderingOrder() (DG);
0143: * 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data
0144: * area) (DG);
0145: * 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument
0146: * ignored) (DG);
0147: * 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and
0148: * setRangeCrosshairStroke(), fixed clipping for
0149: * anntotations (DG);
0150: * 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG);
0151: *
0152: */
0153:
0154: package org.jfree.chart.plot;
0155:
0156: import java.awt.AlphaComposite;
0157: import java.awt.BasicStroke;
0158: import java.awt.Color;
0159: import java.awt.Composite;
0160: import java.awt.Font;
0161: import java.awt.Graphics2D;
0162: import java.awt.Paint;
0163: import java.awt.Shape;
0164: import java.awt.Stroke;
0165: import java.awt.geom.Line2D;
0166: import java.awt.geom.Point2D;
0167: import java.awt.geom.Rectangle2D;
0168: import java.io.IOException;
0169: import java.io.ObjectInputStream;
0170: import java.io.ObjectOutputStream;
0171: import java.io.Serializable;
0172: import java.util.ArrayList;
0173: import java.util.Collection;
0174: import java.util.Collections;
0175: import java.util.HashMap;
0176: import java.util.Iterator;
0177: import java.util.List;
0178: import java.util.Map;
0179: import java.util.ResourceBundle;
0180: import java.util.Set;
0181:
0182: import org.jfree.chart.LegendItem;
0183: import org.jfree.chart.LegendItemCollection;
0184: import org.jfree.chart.annotations.CategoryAnnotation;
0185: import org.jfree.chart.axis.Axis;
0186: import org.jfree.chart.axis.AxisCollection;
0187: import org.jfree.chart.axis.AxisLocation;
0188: import org.jfree.chart.axis.AxisSpace;
0189: import org.jfree.chart.axis.AxisState;
0190: import org.jfree.chart.axis.CategoryAnchor;
0191: import org.jfree.chart.axis.CategoryAxis;
0192: import org.jfree.chart.axis.ValueAxis;
0193: import org.jfree.chart.axis.ValueTick;
0194: import org.jfree.chart.event.ChartChangeEventType;
0195: import org.jfree.chart.event.PlotChangeEvent;
0196: import org.jfree.chart.event.RendererChangeEvent;
0197: import org.jfree.chart.event.RendererChangeListener;
0198: import org.jfree.chart.renderer.category.CategoryItemRenderer;
0199: import org.jfree.chart.renderer.category.CategoryItemRendererState;
0200: import org.jfree.data.Range;
0201: import org.jfree.data.category.CategoryDataset;
0202: import org.jfree.data.general.Dataset;
0203: import org.jfree.data.general.DatasetChangeEvent;
0204: import org.jfree.data.general.DatasetUtilities;
0205: import org.jfree.io.SerialUtilities;
0206: import org.jfree.ui.Layer;
0207: import org.jfree.ui.RectangleEdge;
0208: import org.jfree.ui.RectangleInsets;
0209: import org.jfree.util.ObjectList;
0210: import org.jfree.util.ObjectUtilities;
0211: import org.jfree.util.PaintUtilities;
0212: import org.jfree.util.PublicCloneable;
0213: import org.jfree.util.SortOrder;
0214:
0215: /**
0216: * A general plotting class that uses data from a {@link CategoryDataset} and
0217: * renders each data item using a {@link CategoryItemRenderer}.
0218: */
0219: public class CategoryPlot extends Plot implements ValueAxisPlot,
0220: Zoomable, RendererChangeListener, Cloneable, PublicCloneable,
0221: Serializable {
0222:
0223: /** For serialization. */
0224: private static final long serialVersionUID = -3537691700434728188L;
0225:
0226: /**
0227: * The default visibility of the grid lines plotted against the domain
0228: * axis.
0229: */
0230: public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false;
0231:
0232: /**
0233: * The default visibility of the grid lines plotted against the range
0234: * axis.
0235: */
0236: public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true;
0237:
0238: /** The default grid line stroke. */
0239: public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(
0240: 0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
0241: new float[] { 2.0f, 2.0f }, 0.0f);
0242:
0243: /** The default grid line paint. */
0244: public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
0245:
0246: /** The default value label font. */
0247: public static final Font DEFAULT_VALUE_LABEL_FONT = new Font(
0248: "SansSerif", Font.PLAIN, 10);
0249:
0250: /**
0251: * The default crosshair visibility.
0252: *
0253: * @since 1.0.5
0254: */
0255: public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
0256:
0257: /**
0258: * The default crosshair stroke.
0259: *
0260: * @since 1.0.5
0261: */
0262: public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE;
0263:
0264: /**
0265: * The default crosshair paint.
0266: *
0267: * @since 1.0.5
0268: */
0269: public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
0270:
0271: /** The resourceBundle for the localization. */
0272: protected static ResourceBundle localizationResources = ResourceBundle
0273: .getBundle("org.jfree.chart.plot.LocalizationBundle");
0274:
0275: /** The plot orientation. */
0276: private PlotOrientation orientation;
0277:
0278: /** The offset between the data area and the axes. */
0279: private RectangleInsets axisOffset;
0280:
0281: /** Storage for the domain axes. */
0282: private ObjectList domainAxes;
0283:
0284: /** Storage for the domain axis locations. */
0285: private ObjectList domainAxisLocations;
0286:
0287: /**
0288: * A flag that controls whether or not the shared domain axis is drawn
0289: * (only relevant when the plot is being used as a subplot).
0290: */
0291: private boolean drawSharedDomainAxis;
0292:
0293: /** Storage for the range axes. */
0294: private ObjectList rangeAxes;
0295:
0296: /** Storage for the range axis locations. */
0297: private ObjectList rangeAxisLocations;
0298:
0299: /** Storage for the datasets. */
0300: private ObjectList datasets;
0301:
0302: /** Storage for keys that map datasets to domain axes. */
0303: private ObjectList datasetToDomainAxisMap;
0304:
0305: /** Storage for keys that map datasets to range axes. */
0306: private ObjectList datasetToRangeAxisMap;
0307:
0308: /** Storage for the renderers. */
0309: private ObjectList renderers;
0310:
0311: /** The dataset rendering order. */
0312: private DatasetRenderingOrder renderingOrder = DatasetRenderingOrder.REVERSE;
0313:
0314: /**
0315: * Controls the order in which the columns are traversed when rendering the
0316: * data items.
0317: */
0318: private SortOrder columnRenderingOrder = SortOrder.ASCENDING;
0319:
0320: /**
0321: * Controls the order in which the rows are traversed when rendering the
0322: * data items.
0323: */
0324: private SortOrder rowRenderingOrder = SortOrder.ASCENDING;
0325:
0326: /**
0327: * A flag that controls whether the grid-lines for the domain axis are
0328: * visible.
0329: */
0330: private boolean domainGridlinesVisible;
0331:
0332: /** The position of the domain gridlines relative to the category. */
0333: private CategoryAnchor domainGridlinePosition;
0334:
0335: /** The stroke used to draw the domain grid-lines. */
0336: private transient Stroke domainGridlineStroke;
0337:
0338: /** The paint used to draw the domain grid-lines. */
0339: private transient Paint domainGridlinePaint;
0340:
0341: /**
0342: * A flag that controls whether the grid-lines for the range axis are
0343: * visible.
0344: */
0345: private boolean rangeGridlinesVisible;
0346:
0347: /** The stroke used to draw the range axis grid-lines. */
0348: private transient Stroke rangeGridlineStroke;
0349:
0350: /** The paint used to draw the range axis grid-lines. */
0351: private transient Paint rangeGridlinePaint;
0352:
0353: /** The anchor value. */
0354: private double anchorValue;
0355:
0356: /** A flag that controls whether or not a range crosshair is drawn. */
0357: private boolean rangeCrosshairVisible;
0358:
0359: /** The range crosshair value. */
0360: private double rangeCrosshairValue;
0361:
0362: /** The pen/brush used to draw the crosshair (if any). */
0363: private transient Stroke rangeCrosshairStroke;
0364:
0365: /** The color used to draw the crosshair (if any). */
0366: private transient Paint rangeCrosshairPaint;
0367:
0368: /**
0369: * A flag that controls whether or not the crosshair locks onto actual
0370: * data points.
0371: */
0372: private boolean rangeCrosshairLockedOnData = true;
0373:
0374: /** A map containing lists of markers for the domain axes. */
0375: private Map foregroundDomainMarkers;
0376:
0377: /** A map containing lists of markers for the domain axes. */
0378: private Map backgroundDomainMarkers;
0379:
0380: /** A map containing lists of markers for the range axes. */
0381: private Map foregroundRangeMarkers;
0382:
0383: /** A map containing lists of markers for the range axes. */
0384: private Map backgroundRangeMarkers;
0385:
0386: /**
0387: * A (possibly empty) list of annotations for the plot. The list should
0388: * be initialised in the constructor and never allowed to be
0389: * <code>null</code>.
0390: */
0391: private List annotations;
0392:
0393: /**
0394: * The weight for the plot (only relevant when the plot is used as a subplot
0395: * within a combined plot).
0396: */
0397: private int weight;
0398:
0399: /** The fixed space for the domain axis. */
0400: private AxisSpace fixedDomainAxisSpace;
0401:
0402: /** The fixed space for the range axis. */
0403: private AxisSpace fixedRangeAxisSpace;
0404:
0405: /**
0406: * An optional collection of legend items that can be returned by the
0407: * getLegendItems() method.
0408: */
0409: private LegendItemCollection fixedLegendItems;
0410:
0411: /**
0412: * Default constructor.
0413: */
0414: public CategoryPlot() {
0415: this (null, null, null, null);
0416: }
0417:
0418: /**
0419: * Creates a new plot.
0420: *
0421: * @param dataset the dataset (<code>null</code> permitted).
0422: * @param domainAxis the domain axis (<code>null</code> permitted).
0423: * @param rangeAxis the range axis (<code>null</code> permitted).
0424: * @param renderer the item renderer (<code>null</code> permitted).
0425: *
0426: */
0427: public CategoryPlot(CategoryDataset dataset,
0428: CategoryAxis domainAxis, ValueAxis rangeAxis,
0429: CategoryItemRenderer renderer) {
0430:
0431: super ();
0432:
0433: this .orientation = PlotOrientation.VERTICAL;
0434:
0435: // allocate storage for dataset, axes and renderers
0436: this .domainAxes = new ObjectList();
0437: this .domainAxisLocations = new ObjectList();
0438: this .rangeAxes = new ObjectList();
0439: this .rangeAxisLocations = new ObjectList();
0440:
0441: this .datasetToDomainAxisMap = new ObjectList();
0442: this .datasetToRangeAxisMap = new ObjectList();
0443:
0444: this .renderers = new ObjectList();
0445:
0446: this .datasets = new ObjectList();
0447: this .datasets.set(0, dataset);
0448: if (dataset != null) {
0449: dataset.addChangeListener(this );
0450: }
0451:
0452: this .axisOffset = RectangleInsets.ZERO_INSETS;
0453:
0454: setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false);
0455: setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false);
0456:
0457: this .renderers.set(0, renderer);
0458: if (renderer != null) {
0459: renderer.setPlot(this );
0460: renderer.addChangeListener(this );
0461: }
0462:
0463: this .domainAxes.set(0, domainAxis);
0464: this .mapDatasetToDomainAxis(0, 0);
0465: if (domainAxis != null) {
0466: domainAxis.setPlot(this );
0467: domainAxis.addChangeListener(this );
0468: }
0469: this .drawSharedDomainAxis = false;
0470:
0471: this .rangeAxes.set(0, rangeAxis);
0472: this .mapDatasetToRangeAxis(0, 0);
0473: if (rangeAxis != null) {
0474: rangeAxis.setPlot(this );
0475: rangeAxis.addChangeListener(this );
0476: }
0477:
0478: configureDomainAxes();
0479: configureRangeAxes();
0480:
0481: this .domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE;
0482: this .domainGridlinePosition = CategoryAnchor.MIDDLE;
0483: this .domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0484: this .domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0485:
0486: this .rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE;
0487: this .rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0488: this .rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0489:
0490: this .foregroundDomainMarkers = new HashMap();
0491: this .backgroundDomainMarkers = new HashMap();
0492: this .foregroundRangeMarkers = new HashMap();
0493: this .backgroundRangeMarkers = new HashMap();
0494:
0495: Marker baseline = new ValueMarker(0.0, new Color(0.8f, 0.8f,
0496: 0.8f, 0.5f), new BasicStroke(1.0f), new Color(0.85f,
0497: 0.85f, 0.95f, 0.5f), new BasicStroke(1.0f), 0.6f);
0498: addRangeMarker(baseline, Layer.BACKGROUND);
0499:
0500: this .anchorValue = 0.0;
0501:
0502: this .rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE;
0503: this .rangeCrosshairValue = 0.0;
0504: this .rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
0505: this .rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
0506:
0507: this .annotations = new java.util.ArrayList();
0508:
0509: }
0510:
0511: /**
0512: * Returns a string describing the type of plot.
0513: *
0514: * @return The type.
0515: */
0516: public String getPlotType() {
0517: return localizationResources.getString("Category_Plot");
0518: }
0519:
0520: /**
0521: * Returns the orientation of the plot.
0522: *
0523: * @return The orientation of the plot (never <code>null</code>).
0524: *
0525: * @see #setOrientation(PlotOrientation)
0526: */
0527: public PlotOrientation getOrientation() {
0528: return this .orientation;
0529: }
0530:
0531: /**
0532: * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
0533: * all registered listeners.
0534: *
0535: * @param orientation the orientation (<code>null</code> not permitted).
0536: *
0537: * @see #getOrientation()
0538: */
0539: public void setOrientation(PlotOrientation orientation) {
0540: if (orientation == null) {
0541: throw new IllegalArgumentException(
0542: "Null 'orientation' argument.");
0543: }
0544: this .orientation = orientation;
0545: notifyListeners(new PlotChangeEvent(this ));
0546: }
0547:
0548: /**
0549: * Returns the axis offset.
0550: *
0551: * @return The axis offset (never <code>null</code>).
0552: *
0553: * @see #setAxisOffset(RectangleInsets)
0554: */
0555: public RectangleInsets getAxisOffset() {
0556: return this .axisOffset;
0557: }
0558:
0559: /**
0560: * Sets the axis offsets (gap between the data area and the axes) and
0561: * sends a {@link PlotChangeEvent} to all registered listeners.
0562: *
0563: * @param offset the offset (<code>null</code> not permitted).
0564: *
0565: * @see #getAxisOffset()
0566: */
0567: public void setAxisOffset(RectangleInsets offset) {
0568: if (offset == null) {
0569: throw new IllegalArgumentException(
0570: "Null 'offset' argument.");
0571: }
0572: this .axisOffset = offset;
0573: notifyListeners(new PlotChangeEvent(this ));
0574: }
0575:
0576: /**
0577: * Returns the domain axis for the plot. If the domain axis for this plot
0578: * is <code>null</code>, then the method will return the parent plot's
0579: * domain axis (if there is a parent plot).
0580: *
0581: * @return The domain axis (<code>null</code> permitted).
0582: *
0583: * @see #setDomainAxis(CategoryAxis)
0584: */
0585: public CategoryAxis getDomainAxis() {
0586: return getDomainAxis(0);
0587: }
0588:
0589: /**
0590: * Returns a domain axis.
0591: *
0592: * @param index the axis index.
0593: *
0594: * @return The axis (<code>null</code> possible).
0595: *
0596: * @see #setDomainAxis(int, CategoryAxis)
0597: */
0598: public CategoryAxis getDomainAxis(int index) {
0599: CategoryAxis result = null;
0600: if (index < this .domainAxes.size()) {
0601: result = (CategoryAxis) this .domainAxes.get(index);
0602: }
0603: if (result == null) {
0604: Plot parent = getParent();
0605: if (parent instanceof CategoryPlot) {
0606: CategoryPlot cp = (CategoryPlot) parent;
0607: result = cp.getDomainAxis(index);
0608: }
0609: }
0610: return result;
0611: }
0612:
0613: /**
0614: * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to
0615: * all registered listeners.
0616: *
0617: * @param axis the axis (<code>null</code> permitted).
0618: *
0619: * @see #getDomainAxis()
0620: */
0621: public void setDomainAxis(CategoryAxis axis) {
0622: setDomainAxis(0, axis);
0623: }
0624:
0625: /**
0626: * Sets a domain axis and sends a {@link PlotChangeEvent} to all
0627: * registered listeners.
0628: *
0629: * @param index the axis index.
0630: * @param axis the axis (<code>null</code> permitted).
0631: *
0632: * @see #getDomainAxis(int)
0633: */
0634: public void setDomainAxis(int index, CategoryAxis axis) {
0635: setDomainAxis(index, axis, true);
0636: }
0637:
0638: /**
0639: * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
0640: * all registered listeners.
0641: *
0642: * @param index the axis index.
0643: * @param axis the axis (<code>null</code> permitted).
0644: * @param notify notify listeners?
0645: */
0646: public void setDomainAxis(int index, CategoryAxis axis,
0647: boolean notify) {
0648: CategoryAxis existing = (CategoryAxis) this .domainAxes
0649: .get(index);
0650: if (existing != null) {
0651: existing.removeChangeListener(this );
0652: }
0653: if (axis != null) {
0654: axis.setPlot(this );
0655: }
0656: this .domainAxes.set(index, axis);
0657: if (axis != null) {
0658: axis.configure();
0659: axis.addChangeListener(this );
0660: }
0661: if (notify) {
0662: notifyListeners(new PlotChangeEvent(this ));
0663: }
0664: }
0665:
0666: /**
0667: * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
0668: * to all registered listeners.
0669: *
0670: * @param axes the axes (<code>null</code> not permitted).
0671: *
0672: * @see #setRangeAxes(ValueAxis[])
0673: */
0674: public void setDomainAxes(CategoryAxis[] axes) {
0675: for (int i = 0; i < axes.length; i++) {
0676: setDomainAxis(i, axes[i], false);
0677: }
0678: notifyListeners(new PlotChangeEvent(this ));
0679: }
0680:
0681: /**
0682: * Returns the index of the specified axis, or <code>-1</code> if the axis
0683: * is not assigned to the plot.
0684: *
0685: * @param axis the axis.
0686: *
0687: * @return The axis index.
0688: *
0689: * @since 1.0.3
0690: */
0691: public int getDomainAxisIndex(CategoryAxis axis) {
0692: return this .domainAxes.indexOf(axis);
0693: }
0694:
0695: /**
0696: * Returns the domain axis location for the primary domain axis.
0697: *
0698: * @return The location (never <code>null</code>).
0699: *
0700: * @see #getRangeAxisLocation()
0701: */
0702: public AxisLocation getDomainAxisLocation() {
0703: return getDomainAxisLocation(0);
0704: }
0705:
0706: /**
0707: * Returns the location for a domain axis.
0708: *
0709: * @param index the axis index.
0710: *
0711: * @return The location.
0712: *
0713: * @see #setDomainAxisLocation(int, AxisLocation)
0714: */
0715: public AxisLocation getDomainAxisLocation(int index) {
0716: AxisLocation result = null;
0717: if (index < this .domainAxisLocations.size()) {
0718: result = (AxisLocation) this .domainAxisLocations.get(index);
0719: }
0720: if (result == null) {
0721: result = AxisLocation.getOpposite(getDomainAxisLocation(0));
0722: }
0723: return result;
0724: }
0725:
0726: /**
0727: * Sets the location of the domain axis and sends a {@link PlotChangeEvent}
0728: * to all registered listeners.
0729: *
0730: * @param location the axis location (<code>null</code> not permitted).
0731: *
0732: * @see #getDomainAxisLocation()
0733: * @see #setDomainAxisLocation(int, AxisLocation)
0734: */
0735: public void setDomainAxisLocation(AxisLocation location) {
0736: // delegate...
0737: setDomainAxisLocation(0, location, true);
0738: }
0739:
0740: /**
0741: * Sets the location of the domain axis and, if requested, sends a
0742: * {@link PlotChangeEvent} to all registered listeners.
0743: *
0744: * @param location the axis location (<code>null</code> not permitted).
0745: * @param notify a flag that controls whether listeners are notified.
0746: */
0747: public void setDomainAxisLocation(AxisLocation location,
0748: boolean notify) {
0749: // delegate...
0750: setDomainAxisLocation(0, location, notify);
0751: }
0752:
0753: /**
0754: * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
0755: * to all registered listeners.
0756: *
0757: * @param index the axis index.
0758: * @param location the location.
0759: *
0760: * @see #getDomainAxisLocation(int)
0761: * @see #setRangeAxisLocation(int, AxisLocation)
0762: */
0763: public void setDomainAxisLocation(int index, AxisLocation location) {
0764: // delegate...
0765: setDomainAxisLocation(index, location, true);
0766: }
0767:
0768: /**
0769: * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
0770: * to all registered listeners.
0771: *
0772: * @param index the axis index.
0773: * @param location the location.
0774: * @param notify notify listeners?
0775: *
0776: * @since 1.0.5
0777: *
0778: * @see #getDomainAxisLocation(int)
0779: * @see #setRangeAxisLocation(int, AxisLocation, boolean)
0780: */
0781: public void setDomainAxisLocation(int index, AxisLocation location,
0782: boolean notify) {
0783: if (index == 0 && location == null) {
0784: throw new IllegalArgumentException(
0785: "Null 'location' for index 0 not permitted.");
0786: }
0787: this .domainAxisLocations.set(index, location);
0788: if (notify) {
0789: notifyListeners(new PlotChangeEvent(this ));
0790: }
0791: }
0792:
0793: /**
0794: * Returns the domain axis edge. This is derived from the axis location
0795: * and the plot orientation.
0796: *
0797: * @return The edge (never <code>null</code>).
0798: */
0799: public RectangleEdge getDomainAxisEdge() {
0800: return getDomainAxisEdge(0);
0801: }
0802:
0803: /**
0804: * Returns the edge for a domain axis.
0805: *
0806: * @param index the axis index.
0807: *
0808: * @return The edge (never <code>null</code>).
0809: */
0810: public RectangleEdge getDomainAxisEdge(int index) {
0811: RectangleEdge result = null;
0812: AxisLocation location = getDomainAxisLocation(index);
0813: if (location != null) {
0814: result = Plot.resolveDomainAxisLocation(location,
0815: this .orientation);
0816: } else {
0817: result = RectangleEdge.opposite(getDomainAxisEdge(0));
0818: }
0819: return result;
0820: }
0821:
0822: /**
0823: * Returns the number of domain axes.
0824: *
0825: * @return The axis count.
0826: */
0827: public int getDomainAxisCount() {
0828: return this .domainAxes.size();
0829: }
0830:
0831: /**
0832: * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
0833: * to all registered listeners.
0834: */
0835: public void clearDomainAxes() {
0836: for (int i = 0; i < this .domainAxes.size(); i++) {
0837: CategoryAxis axis = (CategoryAxis) this .domainAxes.get(i);
0838: if (axis != null) {
0839: axis.removeChangeListener(this );
0840: }
0841: }
0842: this .domainAxes.clear();
0843: notifyListeners(new PlotChangeEvent(this ));
0844: }
0845:
0846: /**
0847: * Configures the domain axes.
0848: */
0849: public void configureDomainAxes() {
0850: for (int i = 0; i < this .domainAxes.size(); i++) {
0851: CategoryAxis axis = (CategoryAxis) this .domainAxes.get(i);
0852: if (axis != null) {
0853: axis.configure();
0854: }
0855: }
0856: }
0857:
0858: /**
0859: * Returns the range axis for the plot. If the range axis for this plot is
0860: * null, then the method will return the parent plot's range axis (if there
0861: * is a parent plot).
0862: *
0863: * @return The range axis (possibly <code>null</code>).
0864: */
0865: public ValueAxis getRangeAxis() {
0866: return getRangeAxis(0);
0867: }
0868:
0869: /**
0870: * Returns a range axis.
0871: *
0872: * @param index the axis index.
0873: *
0874: * @return The axis (<code>null</code> possible).
0875: */
0876: public ValueAxis getRangeAxis(int index) {
0877: ValueAxis result = null;
0878: if (index < this .rangeAxes.size()) {
0879: result = (ValueAxis) this .rangeAxes.get(index);
0880: }
0881: if (result == null) {
0882: Plot parent = getParent();
0883: if (parent instanceof CategoryPlot) {
0884: CategoryPlot cp = (CategoryPlot) parent;
0885: result = cp.getRangeAxis(index);
0886: }
0887: }
0888: return result;
0889: }
0890:
0891: /**
0892: * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
0893: * all registered listeners.
0894: *
0895: * @param axis the axis (<code>null</code> permitted).
0896: */
0897: public void setRangeAxis(ValueAxis axis) {
0898: setRangeAxis(0, axis);
0899: }
0900:
0901: /**
0902: * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
0903: * listeners.
0904: *
0905: * @param index the axis index.
0906: * @param axis the axis.
0907: */
0908: public void setRangeAxis(int index, ValueAxis axis) {
0909: setRangeAxis(index, axis, true);
0910: }
0911:
0912: /**
0913: * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
0914: * all registered listeners.
0915: *
0916: * @param index the axis index.
0917: * @param axis the axis.
0918: * @param notify notify listeners?
0919: */
0920: public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
0921: ValueAxis existing = (ValueAxis) this .rangeAxes.get(index);
0922: if (existing != null) {
0923: existing.removeChangeListener(this );
0924: }
0925: if (axis != null) {
0926: axis.setPlot(this );
0927: }
0928: this .rangeAxes.set(index, axis);
0929: if (axis != null) {
0930: axis.configure();
0931: axis.addChangeListener(this );
0932: }
0933: if (notify) {
0934: notifyListeners(new PlotChangeEvent(this ));
0935: }
0936: }
0937:
0938: /**
0939: * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
0940: * to all registered listeners.
0941: *
0942: * @param axes the axes (<code>null</code> not permitted).
0943: *
0944: * @see #setDomainAxes(CategoryAxis[])
0945: */
0946: public void setRangeAxes(ValueAxis[] axes) {
0947: for (int i = 0; i < axes.length; i++) {
0948: setRangeAxis(i, axes[i], false);
0949: }
0950: notifyListeners(new PlotChangeEvent(this ));
0951: }
0952:
0953: /**
0954: * Returns the range axis location.
0955: *
0956: * @return The location (never <code>null</code>).
0957: */
0958: public AxisLocation getRangeAxisLocation() {
0959: return getRangeAxisLocation(0);
0960: }
0961:
0962: /**
0963: * Returns the location for a range axis.
0964: *
0965: * @param index the axis index.
0966: *
0967: * @return The location.
0968: *
0969: * @see #setRangeAxisLocation(int, AxisLocation)
0970: */
0971: public AxisLocation getRangeAxisLocation(int index) {
0972: AxisLocation result = null;
0973: if (index < this .rangeAxisLocations.size()) {
0974: result = (AxisLocation) this .rangeAxisLocations.get(index);
0975: }
0976: if (result == null) {
0977: result = AxisLocation.getOpposite(getRangeAxisLocation(0));
0978: }
0979: return result;
0980: }
0981:
0982: /**
0983: * Sets the location of the range axis and sends a {@link PlotChangeEvent}
0984: * to all registered listeners.
0985: *
0986: * @param location the location (<code>null</code> not permitted).
0987: *
0988: * @see #setRangeAxisLocation(AxisLocation, boolean)
0989: * @see #setDomainAxisLocation(AxisLocation)
0990: */
0991: public void setRangeAxisLocation(AxisLocation location) {
0992: // defer argument checking...
0993: setRangeAxisLocation(location, true);
0994: }
0995:
0996: /**
0997: * Sets the location of the range axis and, if requested, sends a
0998: * {@link PlotChangeEvent} to all registered listeners.
0999: *
1000: * @param location the location (<code>null</code> not permitted).
1001: * @param notify notify listeners?
1002: *
1003: * @see #setDomainAxisLocation(AxisLocation, boolean)
1004: */
1005: public void setRangeAxisLocation(AxisLocation location,
1006: boolean notify) {
1007: setRangeAxisLocation(0, location, notify);
1008: }
1009:
1010: /**
1011: * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1012: * to all registered listeners.
1013: *
1014: * @param index the axis index.
1015: * @param location the location.
1016: *
1017: * @see #getRangeAxisLocation(int)
1018: * @see #setRangeAxisLocation(int, AxisLocation, boolean)
1019: */
1020: public void setRangeAxisLocation(int index, AxisLocation location) {
1021: setRangeAxisLocation(index, location, true);
1022: }
1023:
1024: /**
1025: * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1026: * to all registered listeners.
1027: *
1028: * @param index the axis index.
1029: * @param location the location.
1030: * @param notify notify listeners?
1031: *
1032: * @see #getRangeAxisLocation(int)
1033: * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1034: */
1035: public void setRangeAxisLocation(int index, AxisLocation location,
1036: boolean notify) {
1037: if (index == 0 && location == null) {
1038: throw new IllegalArgumentException(
1039: "Null 'location' for index 0 not permitted.");
1040: }
1041: this .rangeAxisLocations.set(index, location);
1042: if (notify) {
1043: notifyListeners(new PlotChangeEvent(this ));
1044: }
1045: }
1046:
1047: /**
1048: * Returns the edge where the primary range axis is located.
1049: *
1050: * @return The edge (never <code>null</code>).
1051: */
1052: public RectangleEdge getRangeAxisEdge() {
1053: return getRangeAxisEdge(0);
1054: }
1055:
1056: /**
1057: * Returns the edge for a range axis.
1058: *
1059: * @param index the axis index.
1060: *
1061: * @return The edge.
1062: */
1063: public RectangleEdge getRangeAxisEdge(int index) {
1064: AxisLocation location = getRangeAxisLocation(index);
1065: RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1066: this .orientation);
1067: if (result == null) {
1068: result = RectangleEdge.opposite(getRangeAxisEdge(0));
1069: }
1070: return result;
1071: }
1072:
1073: /**
1074: * Returns the number of range axes.
1075: *
1076: * @return The axis count.
1077: */
1078: public int getRangeAxisCount() {
1079: return this .rangeAxes.size();
1080: }
1081:
1082: /**
1083: * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1084: * to all registered listeners.
1085: */
1086: public void clearRangeAxes() {
1087: for (int i = 0; i < this .rangeAxes.size(); i++) {
1088: ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1089: if (axis != null) {
1090: axis.removeChangeListener(this );
1091: }
1092: }
1093: this .rangeAxes.clear();
1094: notifyListeners(new PlotChangeEvent(this ));
1095: }
1096:
1097: /**
1098: * Configures the range axes.
1099: */
1100: public void configureRangeAxes() {
1101: for (int i = 0; i < this .rangeAxes.size(); i++) {
1102: ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1103: if (axis != null) {
1104: axis.configure();
1105: }
1106: }
1107: }
1108:
1109: /**
1110: * Returns the primary dataset for the plot.
1111: *
1112: * @return The primary dataset (possibly <code>null</code>).
1113: *
1114: * @see #setDataset(CategoryDataset)
1115: */
1116: public CategoryDataset getDataset() {
1117: return getDataset(0);
1118: }
1119:
1120: /**
1121: * Returns the dataset at the given index.
1122: *
1123: * @param index the dataset index.
1124: *
1125: * @return The dataset (possibly <code>null</code>).
1126: *
1127: * @see #setDataset(int, CategoryDataset)
1128: */
1129: public CategoryDataset getDataset(int index) {
1130: CategoryDataset result = null;
1131: if (this .datasets.size() > index) {
1132: result = (CategoryDataset) this .datasets.get(index);
1133: }
1134: return result;
1135: }
1136:
1137: /**
1138: * Sets the dataset for the plot, replacing the existing dataset, if there
1139: * is one. This method also calls the
1140: * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the
1141: * axis ranges if necessary and sends a {@link PlotChangeEvent} to all
1142: * registered listeners.
1143: *
1144: * @param dataset the dataset (<code>null</code> permitted).
1145: *
1146: * @see #getDataset()
1147: */
1148: public void setDataset(CategoryDataset dataset) {
1149: setDataset(0, dataset);
1150: }
1151:
1152: /**
1153: * Sets a dataset for the plot.
1154: *
1155: * @param index the dataset index.
1156: * @param dataset the dataset (<code>null</code> permitted).
1157: *
1158: * @see #getDataset(int)
1159: */
1160: public void setDataset(int index, CategoryDataset dataset) {
1161:
1162: CategoryDataset existing = (CategoryDataset) this .datasets
1163: .get(index);
1164: if (existing != null) {
1165: existing.removeChangeListener(this );
1166: }
1167: this .datasets.set(index, dataset);
1168: if (dataset != null) {
1169: dataset.addChangeListener(this );
1170: }
1171:
1172: // send a dataset change event to self...
1173: DatasetChangeEvent event = new DatasetChangeEvent(this , dataset);
1174: datasetChanged(event);
1175:
1176: }
1177:
1178: /**
1179: * Returns the number of datasets.
1180: *
1181: * @return The number of datasets.
1182: *
1183: * @since 1.0.2
1184: */
1185: public int getDatasetCount() {
1186: return this .datasets.size();
1187: }
1188:
1189: /**
1190: * Maps a dataset to a particular domain axis.
1191: *
1192: * @param index the dataset index (zero-based).
1193: * @param axisIndex the axis index (zero-based).
1194: *
1195: * @see #getDomainAxisForDataset(int)
1196: */
1197: public void mapDatasetToDomainAxis(int index, int axisIndex) {
1198: this .datasetToDomainAxisMap.set(index, new Integer(axisIndex));
1199: // fake a dataset change event to update axes...
1200: datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1201: }
1202:
1203: /**
1204: * Returns the domain axis for a dataset. You can change the axis for a
1205: * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method.
1206: *
1207: * @param index the dataset index.
1208: *
1209: * @return The domain axis.
1210: *
1211: * @see #mapDatasetToDomainAxis(int, int)
1212: */
1213: public CategoryAxis getDomainAxisForDataset(int index) {
1214: CategoryAxis result = getDomainAxis();
1215: Integer axisIndex = (Integer) this .datasetToDomainAxisMap
1216: .get(index);
1217: if (axisIndex != null) {
1218: result = getDomainAxis(axisIndex.intValue());
1219: }
1220: return result;
1221: }
1222:
1223: /**
1224: * Maps a dataset to a particular range axis.
1225: *
1226: * @param index the dataset index (zero-based).
1227: * @param axisIndex the axis index (zero-based).
1228: *
1229: * @see #getRangeAxisForDataset(int)
1230: */
1231: public void mapDatasetToRangeAxis(int index, int axisIndex) {
1232: this .datasetToRangeAxisMap.set(index, new Integer(axisIndex));
1233: // fake a dataset change event to update axes...
1234: datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1235: }
1236:
1237: /**
1238: * Returns the range axis for a dataset. You can change the axis for a
1239: * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method.
1240: *
1241: * @param index the dataset index.
1242: *
1243: * @return The range axis.
1244: *
1245: * @see #mapDatasetToRangeAxis(int, int)
1246: */
1247: public ValueAxis getRangeAxisForDataset(int index) {
1248: ValueAxis result = getRangeAxis();
1249: Integer axisIndex = (Integer) this .datasetToRangeAxisMap
1250: .get(index);
1251: if (axisIndex != null) {
1252: result = getRangeAxis(axisIndex.intValue());
1253: }
1254: return result;
1255: }
1256:
1257: /**
1258: * Returns a reference to the renderer for the plot.
1259: *
1260: * @return The renderer.
1261: *
1262: * @see #setRenderer(CategoryItemRenderer)
1263: */
1264: public CategoryItemRenderer getRenderer() {
1265: return getRenderer(0);
1266: }
1267:
1268: /**
1269: * Returns the renderer at the given index.
1270: *
1271: * @param index the renderer index.
1272: *
1273: * @return The renderer (possibly <code>null</code>).
1274: *
1275: * @see #setRenderer(int, CategoryItemRenderer)
1276: */
1277: public CategoryItemRenderer getRenderer(int index) {
1278: CategoryItemRenderer result = null;
1279: if (this .renderers.size() > index) {
1280: result = (CategoryItemRenderer) this .renderers.get(index);
1281: }
1282: return result;
1283: }
1284:
1285: /**
1286: * Sets the renderer at index 0 (sometimes referred to as the "primary"
1287: * renderer) and sends a {@link PlotChangeEvent} to all registered
1288: * listeners.
1289: *
1290: * @param renderer the renderer (<code>null</code> permitted.
1291: *
1292: * @see #getRenderer()
1293: */
1294: public void setRenderer(CategoryItemRenderer renderer) {
1295: setRenderer(0, renderer, true);
1296: }
1297:
1298: /**
1299: * Sets the renderer at index 0 (sometimes referred to as the "primary"
1300: * renderer) and, if requested, sends a {@link PlotChangeEvent} to all
1301: * registered listeners.
1302: * <p>
1303: * You can set the renderer to <code>null</code>, but this is not
1304: * recommended because:
1305: * <ul>
1306: * <li>no data will be displayed;</li>
1307: * <li>the plot background will not be painted;</li>
1308: * </ul>
1309: *
1310: * @param renderer the renderer (<code>null</code> permitted).
1311: * @param notify notify listeners?
1312: *
1313: * @see #getRenderer()
1314: */
1315: public void setRenderer(CategoryItemRenderer renderer,
1316: boolean notify) {
1317: setRenderer(0, renderer, notify);
1318: }
1319:
1320: /**
1321: * Sets the renderer at the specified index and sends a
1322: * {@link PlotChangeEvent} to all registered listeners.
1323: *
1324: * @param index the index.
1325: * @param renderer the renderer (<code>null</code> permitted).
1326: *
1327: * @see #getRenderer(int)
1328: * @see #setRenderer(int, CategoryItemRenderer, boolean)
1329: */
1330: public void setRenderer(int index, CategoryItemRenderer renderer) {
1331: setRenderer(index, renderer, true);
1332: }
1333:
1334: /**
1335: * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered
1336: * listeners.
1337: *
1338: * @param index the index.
1339: * @param renderer the renderer (<code>null</code> permitted).
1340: * @param notify notify listeners?
1341: *
1342: * @see #getRenderer(int)
1343: */
1344: public void setRenderer(int index, CategoryItemRenderer renderer,
1345: boolean notify) {
1346:
1347: // stop listening to the existing renderer...
1348: CategoryItemRenderer existing = (CategoryItemRenderer) this .renderers
1349: .get(index);
1350: if (existing != null) {
1351: existing.removeChangeListener(this );
1352: }
1353:
1354: // register the new renderer...
1355: this .renderers.set(index, renderer);
1356: if (renderer != null) {
1357: renderer.setPlot(this );
1358: renderer.addChangeListener(this );
1359: }
1360:
1361: configureDomainAxes();
1362: configureRangeAxes();
1363:
1364: if (notify) {
1365: notifyListeners(new PlotChangeEvent(this ));
1366: }
1367: }
1368:
1369: /**
1370: * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1371: * to all registered listeners.
1372: *
1373: * @param renderers the renderers.
1374: */
1375: public void setRenderers(CategoryItemRenderer[] renderers) {
1376: for (int i = 0; i < renderers.length; i++) {
1377: setRenderer(i, renderers[i], false);
1378: }
1379: notifyListeners(new PlotChangeEvent(this ));
1380: }
1381:
1382: /**
1383: * Returns the renderer for the specified dataset. If the dataset doesn't
1384: * belong to the plot, this method will return <code>null</code>.
1385: *
1386: * @param dataset the dataset (<code>null</code> permitted).
1387: *
1388: * @return The renderer (possibly <code>null</code>).
1389: */
1390: public CategoryItemRenderer getRendererForDataset(
1391: CategoryDataset dataset) {
1392: CategoryItemRenderer result = null;
1393: for (int i = 0; i < this .datasets.size(); i++) {
1394: if (this .datasets.get(i) == dataset) {
1395: result = (CategoryItemRenderer) this .renderers.get(i);
1396: break;
1397: }
1398: }
1399: return result;
1400: }
1401:
1402: /**
1403: * Returns the index of the specified renderer, or <code>-1</code> if the
1404: * renderer is not assigned to this plot.
1405: *
1406: * @param renderer the renderer (<code>null</code> permitted).
1407: *
1408: * @return The renderer index.
1409: */
1410: public int getIndexOf(CategoryItemRenderer renderer) {
1411: return this .renderers.indexOf(renderer);
1412: }
1413:
1414: /**
1415: * Returns the dataset rendering order.
1416: *
1417: * @return The order (never <code>null</code>).
1418: *
1419: * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1420: */
1421: public DatasetRenderingOrder getDatasetRenderingOrder() {
1422: return this .renderingOrder;
1423: }
1424:
1425: /**
1426: * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1427: * registered listeners. By default, the plot renders the primary dataset
1428: * last (so that the primary dataset overlays the secondary datasets). You
1429: * can reverse this if you want to.
1430: *
1431: * @param order the rendering order (<code>null</code> not permitted).
1432: *
1433: * @see #getDatasetRenderingOrder()
1434: */
1435: public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1436: if (order == null) {
1437: throw new IllegalArgumentException("Null 'order' argument.");
1438: }
1439: this .renderingOrder = order;
1440: notifyListeners(new PlotChangeEvent(this ));
1441: }
1442:
1443: /**
1444: * Returns the order in which the columns are rendered. The default value
1445: * is <code>SortOrder.ASCENDING</code>.
1446: *
1447: * @return The column rendering order (never <code>null</code).
1448: *
1449: * @see #setColumnRenderingOrder(SortOrder)
1450: */
1451: public SortOrder getColumnRenderingOrder() {
1452: return this .columnRenderingOrder;
1453: }
1454:
1455: /**
1456: * Sets the column order in which the items in each dataset should be
1457: * rendered and sends a {@link PlotChangeEvent} to all registered
1458: * listeners. Note that this affects the order in which items are drawn,
1459: * NOT their position in the chart.
1460: *
1461: * @param order the order (<code>null</code> not permitted).
1462: *
1463: * @see #getColumnRenderingOrder()
1464: * @see #setRowRenderingOrder(SortOrder)
1465: */
1466: public void setColumnRenderingOrder(SortOrder order) {
1467: if (order == null) {
1468: throw new IllegalArgumentException("Null 'order' argument.");
1469: }
1470: this .columnRenderingOrder = order;
1471: notifyListeners(new PlotChangeEvent(this ));
1472: }
1473:
1474: /**
1475: * Returns the order in which the rows should be rendered. The default
1476: * value is <code>SortOrder.ASCENDING</code>.
1477: *
1478: * @return The order (never <code>null</code>).
1479: *
1480: * @see #setRowRenderingOrder(SortOrder)
1481: */
1482: public SortOrder getRowRenderingOrder() {
1483: return this .rowRenderingOrder;
1484: }
1485:
1486: /**
1487: * Sets the row order in which the items in each dataset should be
1488: * rendered and sends a {@link PlotChangeEvent} to all registered
1489: * listeners. Note that this affects the order in which items are drawn,
1490: * NOT their position in the chart.
1491: *
1492: * @param order the order (<code>null</code> not permitted).
1493: *
1494: * @see #getRowRenderingOrder()
1495: * @see #setColumnRenderingOrder(SortOrder)
1496: */
1497: public void setRowRenderingOrder(SortOrder order) {
1498: if (order == null) {
1499: throw new IllegalArgumentException("Null 'order' argument.");
1500: }
1501: this .rowRenderingOrder = order;
1502: notifyListeners(new PlotChangeEvent(this ));
1503: }
1504:
1505: /**
1506: * Returns the flag that controls whether the domain grid-lines are visible.
1507: *
1508: * @return The <code>true</code> or <code>false</code>.
1509: *
1510: * @see #setDomainGridlinesVisible(boolean)
1511: */
1512: public boolean isDomainGridlinesVisible() {
1513: return this .domainGridlinesVisible;
1514: }
1515:
1516: /**
1517: * Sets the flag that controls whether or not grid-lines are drawn against
1518: * the domain axis.
1519: * <p>
1520: * If the flag value changes, a {@link PlotChangeEvent} is sent to all
1521: * registered listeners.
1522: *
1523: * @param visible the new value of the flag.
1524: *
1525: * @see #isDomainGridlinesVisible()
1526: */
1527: public void setDomainGridlinesVisible(boolean visible) {
1528: if (this .domainGridlinesVisible != visible) {
1529: this .domainGridlinesVisible = visible;
1530: notifyListeners(new PlotChangeEvent(this ));
1531: }
1532: }
1533:
1534: /**
1535: * Returns the position used for the domain gridlines.
1536: *
1537: * @return The gridline position (never <code>null</code>).
1538: *
1539: * @see #setDomainGridlinePosition(CategoryAnchor)
1540: */
1541: public CategoryAnchor getDomainGridlinePosition() {
1542: return this .domainGridlinePosition;
1543: }
1544:
1545: /**
1546: * Sets the position used for the domain gridlines and sends a
1547: * {@link PlotChangeEvent} to all registered listeners.
1548: *
1549: * @param position the position (<code>null</code> not permitted).
1550: *
1551: * @see #getDomainGridlinePosition()
1552: */
1553: public void setDomainGridlinePosition(CategoryAnchor position) {
1554: if (position == null) {
1555: throw new IllegalArgumentException(
1556: "Null 'position' argument.");
1557: }
1558: this .domainGridlinePosition = position;
1559: notifyListeners(new PlotChangeEvent(this ));
1560: }
1561:
1562: /**
1563: * Returns the stroke used to draw grid-lines against the domain axis.
1564: *
1565: * @return The stroke (never <code>null</code>).
1566: *
1567: * @see #setDomainGridlineStroke(Stroke)
1568: */
1569: public Stroke getDomainGridlineStroke() {
1570: return this .domainGridlineStroke;
1571: }
1572:
1573: /**
1574: * Sets the stroke used to draw grid-lines against the domain axis and
1575: * sends a {@link PlotChangeEvent} to all registered listeners.
1576: *
1577: * @param stroke the stroke (<code>null</code> not permitted).
1578: *
1579: * @see #getDomainGridlineStroke()
1580: */
1581: public void setDomainGridlineStroke(Stroke stroke) {
1582: if (stroke == null) {
1583: throw new IllegalArgumentException(
1584: "Null 'stroke' not permitted.");
1585: }
1586: this .domainGridlineStroke = stroke;
1587: notifyListeners(new PlotChangeEvent(this ));
1588: }
1589:
1590: /**
1591: * Returns the paint used to draw grid-lines against the domain axis.
1592: *
1593: * @return The paint (never <code>null</code>).
1594: *
1595: * @see #setDomainGridlinePaint(Paint)
1596: */
1597: public Paint getDomainGridlinePaint() {
1598: return this .domainGridlinePaint;
1599: }
1600:
1601: /**
1602: * Sets the paint used to draw the grid-lines (if any) against the domain
1603: * axis and sends a {@link PlotChangeEvent} to all registered listeners.
1604: *
1605: * @param paint the paint (<code>null</code> not permitted).
1606: *
1607: * @see #getDomainGridlinePaint()
1608: */
1609: public void setDomainGridlinePaint(Paint paint) {
1610: if (paint == null) {
1611: throw new IllegalArgumentException("Null 'paint' argument.");
1612: }
1613: this .domainGridlinePaint = paint;
1614: notifyListeners(new PlotChangeEvent(this ));
1615: }
1616:
1617: /**
1618: * Returns the flag that controls whether the range grid-lines are visible.
1619: *
1620: * @return The flag.
1621: *
1622: * @see #setRangeGridlinesVisible(boolean)
1623: */
1624: public boolean isRangeGridlinesVisible() {
1625: return this .rangeGridlinesVisible;
1626: }
1627:
1628: /**
1629: * Sets the flag that controls whether or not grid-lines are drawn against
1630: * the range axis. If the flag changes value, a {@link PlotChangeEvent} is
1631: * sent to all registered listeners.
1632: *
1633: * @param visible the new value of the flag.
1634: *
1635: * @see #isRangeGridlinesVisible()
1636: */
1637: public void setRangeGridlinesVisible(boolean visible) {
1638: if (this .rangeGridlinesVisible != visible) {
1639: this .rangeGridlinesVisible = visible;
1640: notifyListeners(new PlotChangeEvent(this ));
1641: }
1642: }
1643:
1644: /**
1645: * Returns the stroke used to draw the grid-lines against the range axis.
1646: *
1647: * @return The stroke (never <code>null</code>).
1648: *
1649: * @see #setRangeGridlineStroke(Stroke)
1650: */
1651: public Stroke getRangeGridlineStroke() {
1652: return this .rangeGridlineStroke;
1653: }
1654:
1655: /**
1656: * Sets the stroke used to draw the grid-lines against the range axis and
1657: * sends a {@link PlotChangeEvent} to all registered listeners.
1658: *
1659: * @param stroke the stroke (<code>null</code> not permitted).
1660: *
1661: * @see #getRangeGridlineStroke()
1662: */
1663: public void setRangeGridlineStroke(Stroke stroke) {
1664: if (stroke == null) {
1665: throw new IllegalArgumentException(
1666: "Null 'stroke' argument.");
1667: }
1668: this .rangeGridlineStroke = stroke;
1669: notifyListeners(new PlotChangeEvent(this ));
1670: }
1671:
1672: /**
1673: * Returns the paint used to draw the grid-lines against the range axis.
1674: *
1675: * @return The paint (never <code>null</code>).
1676: *
1677: * @see #setRangeGridlinePaint(Paint)
1678: */
1679: public Paint getRangeGridlinePaint() {
1680: return this .rangeGridlinePaint;
1681: }
1682:
1683: /**
1684: * Sets the paint used to draw the grid lines against the range axis and
1685: * sends a {@link PlotChangeEvent} to all registered listeners.
1686: *
1687: * @param paint the paint (<code>null</code> not permitted).
1688: *
1689: * @see #getRangeGridlinePaint()
1690: */
1691: public void setRangeGridlinePaint(Paint paint) {
1692: if (paint == null) {
1693: throw new IllegalArgumentException("Null 'paint' argument.");
1694: }
1695: this .rangeGridlinePaint = paint;
1696: notifyListeners(new PlotChangeEvent(this ));
1697: }
1698:
1699: /**
1700: * Returns the fixed legend items, if any.
1701: *
1702: * @return The legend items (possibly <code>null</code>).
1703: *
1704: * @see #setFixedLegendItems(LegendItemCollection)
1705: */
1706: public LegendItemCollection getFixedLegendItems() {
1707: return this .fixedLegendItems;
1708: }
1709:
1710: /**
1711: * Sets the fixed legend items for the plot. Leave this set to
1712: * <code>null</code> if you prefer the legend items to be created
1713: * automatically.
1714: *
1715: * @param items the legend items (<code>null</code> permitted).
1716: *
1717: * @see #getFixedLegendItems()
1718: */
1719: public void setFixedLegendItems(LegendItemCollection items) {
1720: this .fixedLegendItems = items;
1721: notifyListeners(new PlotChangeEvent(this ));
1722: }
1723:
1724: /**
1725: * Returns the legend items for the plot. By default, this method creates
1726: * a legend item for each series in each of the datasets. You can change
1727: * this behaviour by overriding this method.
1728: *
1729: * @return The legend items.
1730: */
1731: public LegendItemCollection getLegendItems() {
1732: LegendItemCollection result = this .fixedLegendItems;
1733: if (result == null) {
1734: result = new LegendItemCollection();
1735: // get the legend items for the datasets...
1736: int count = this .datasets.size();
1737: for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
1738: CategoryDataset dataset = getDataset(datasetIndex);
1739: if (dataset != null) {
1740: CategoryItemRenderer renderer = getRenderer(datasetIndex);
1741: if (renderer != null) {
1742: int seriesCount = dataset.getRowCount();
1743: for (int i = 0; i < seriesCount; i++) {
1744: LegendItem item = renderer.getLegendItem(
1745: datasetIndex, i);
1746: if (item != null) {
1747: result.add(item);
1748: }
1749: }
1750: }
1751: }
1752: }
1753: }
1754: return result;
1755: }
1756:
1757: /**
1758: * Handles a 'click' on the plot by updating the anchor value.
1759: *
1760: * @param x x-coordinate of the click (in Java2D space).
1761: * @param y y-coordinate of the click (in Java2D space).
1762: * @param info information about the plot's dimensions.
1763: *
1764: */
1765: public void handleClick(int x, int y, PlotRenderingInfo info) {
1766:
1767: Rectangle2D dataArea = info.getDataArea();
1768: if (dataArea.contains(x, y)) {
1769: // set the anchor value for the range axis...
1770: double java2D = 0.0;
1771: if (this .orientation == PlotOrientation.HORIZONTAL) {
1772: java2D = x;
1773: } else if (this .orientation == PlotOrientation.VERTICAL) {
1774: java2D = y;
1775: }
1776: RectangleEdge edge = Plot.resolveRangeAxisLocation(
1777: getRangeAxisLocation(), this .orientation);
1778: double value = getRangeAxis().java2DToValue(java2D,
1779: info.getDataArea(), edge);
1780: setAnchorValue(value);
1781: setRangeCrosshairValue(value);
1782: }
1783:
1784: }
1785:
1786: /**
1787: * Zooms (in or out) on the plot's value axis.
1788: * <p>
1789: * If the value 0.0 is passed in as the zoom percent, the auto-range
1790: * calculation for the axis is restored (which sets the range to include
1791: * the minimum and maximum data values, thus displaying all the data).
1792: *
1793: * @param percent the zoom amount.
1794: */
1795: public void zoom(double percent) {
1796:
1797: if (percent > 0.0) {
1798: double range = getRangeAxis().getRange().getLength();
1799: double scaledRange = range * percent;
1800: getRangeAxis().setRange(
1801: this .anchorValue - scaledRange / 2.0,
1802: this .anchorValue + scaledRange / 2.0);
1803: } else {
1804: getRangeAxis().setAutoRange(true);
1805: }
1806:
1807: }
1808:
1809: /**
1810: * Receives notification of a change to the plot's dataset.
1811: * <P>
1812: * The range axis bounds will be recalculated if necessary.
1813: *
1814: * @param event information about the event (not used here).
1815: */
1816: public void datasetChanged(DatasetChangeEvent event) {
1817:
1818: int count = this .rangeAxes.size();
1819: for (int axisIndex = 0; axisIndex < count; axisIndex++) {
1820: ValueAxis yAxis = getRangeAxis(axisIndex);
1821: if (yAxis != null) {
1822: yAxis.configure();
1823: }
1824: }
1825: if (getParent() != null) {
1826: getParent().datasetChanged(event);
1827: } else {
1828: PlotChangeEvent e = new PlotChangeEvent(this );
1829: e.setType(ChartChangeEventType.DATASET_UPDATED);
1830: notifyListeners(e);
1831: }
1832:
1833: }
1834:
1835: /**
1836: * Receives notification of a renderer change event.
1837: *
1838: * @param event the event.
1839: */
1840: public void rendererChanged(RendererChangeEvent event) {
1841: Plot parent = getParent();
1842: if (parent != null) {
1843: if (parent instanceof RendererChangeListener) {
1844: RendererChangeListener rcl = (RendererChangeListener) parent;
1845: rcl.rendererChanged(event);
1846: } else {
1847: // this should never happen with the existing code, but throw
1848: // an exception in case future changes make it possible...
1849: throw new RuntimeException(
1850: "The renderer has changed and I don't know what to do!");
1851: }
1852: } else {
1853: configureRangeAxes();
1854: PlotChangeEvent e = new PlotChangeEvent(this );
1855: notifyListeners(e);
1856: }
1857: }
1858:
1859: /**
1860: * Adds a marker for display (in the foreground) against the domain axis and
1861: * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
1862: * marker will be drawn by the renderer as a line perpendicular to the
1863: * domain axis, however this is entirely up to the renderer.
1864: *
1865: * @param marker the marker (<code>null</code> not permitted).
1866: */
1867: public void addDomainMarker(CategoryMarker marker) {
1868: addDomainMarker(marker, Layer.FOREGROUND);
1869: }
1870:
1871: /**
1872: * Adds a marker for display against the domain axis and sends a
1873: * {@link PlotChangeEvent} to all registered listeners. Typically a marker
1874: * will be drawn by the renderer as a line perpendicular to the domain axis,
1875: * however this is entirely up to the renderer.
1876: *
1877: * @param marker the marker (<code>null</code> not permitted).
1878: * @param layer the layer (foreground or background) (<code>null</code>
1879: * not permitted).
1880: */
1881: public void addDomainMarker(CategoryMarker marker, Layer layer) {
1882: addDomainMarker(0, marker, layer);
1883: }
1884:
1885: /**
1886: * Adds a marker for display by a particular renderer.
1887: * <P>
1888: * Typically a marker will be drawn by the renderer as a line perpendicular
1889: * to a domain axis, however this is entirely up to the renderer.
1890: *
1891: * @param index the renderer index.
1892: * @param marker the marker (<code>null</code> not permitted).
1893: * @param layer the layer (<code>null</code> not permitted).
1894: */
1895: public void addDomainMarker(int index, CategoryMarker marker,
1896: Layer layer) {
1897: if (marker == null) {
1898: throw new IllegalArgumentException(
1899: "Null 'marker' not permitted.");
1900: }
1901: if (layer == null) {
1902: throw new IllegalArgumentException(
1903: "Null 'layer' not permitted.");
1904: }
1905: Collection markers;
1906: if (layer == Layer.FOREGROUND) {
1907: markers = (Collection) this .foregroundDomainMarkers
1908: .get(new Integer(index));
1909: if (markers == null) {
1910: markers = new java.util.ArrayList();
1911: this .foregroundDomainMarkers.put(new Integer(index),
1912: markers);
1913: }
1914: markers.add(marker);
1915: } else if (layer == Layer.BACKGROUND) {
1916: markers = (Collection) this .backgroundDomainMarkers
1917: .get(new Integer(index));
1918: if (markers == null) {
1919: markers = new java.util.ArrayList();
1920: this .backgroundDomainMarkers.put(new Integer(index),
1921: markers);
1922: }
1923: markers.add(marker);
1924: }
1925: marker.addChangeListener(this );
1926: notifyListeners(new PlotChangeEvent(this ));
1927: }
1928:
1929: /**
1930: * Clears all the domain markers for the plot and sends a
1931: * {@link PlotChangeEvent} to all registered listeners.
1932: *
1933: * @see #clearRangeMarkers()
1934: */
1935: public void clearDomainMarkers() {
1936: if (this .backgroundDomainMarkers != null) {
1937: Set keys = this .backgroundDomainMarkers.keySet();
1938: Iterator iterator = keys.iterator();
1939: while (iterator.hasNext()) {
1940: Integer key = (Integer) iterator.next();
1941: clearDomainMarkers(key.intValue());
1942: }
1943: this .backgroundDomainMarkers.clear();
1944: }
1945: if (this .foregroundDomainMarkers != null) {
1946: Set keys = this .foregroundDomainMarkers.keySet();
1947: Iterator iterator = keys.iterator();
1948: while (iterator.hasNext()) {
1949: Integer key = (Integer) iterator.next();
1950: clearDomainMarkers(key.intValue());
1951: }
1952: this .foregroundDomainMarkers.clear();
1953: }
1954: notifyListeners(new PlotChangeEvent(this ));
1955: }
1956:
1957: /**
1958: * Returns the list of domain markers (read only) for the specified layer.
1959: *
1960: * @param layer the layer (foreground or background).
1961: *
1962: * @return The list of domain markers.
1963: */
1964: public Collection getDomainMarkers(Layer layer) {
1965: return getDomainMarkers(0, layer);
1966: }
1967:
1968: /**
1969: * Returns a collection of domain markers for a particular renderer and
1970: * layer.
1971: *
1972: * @param index the renderer index.
1973: * @param layer the layer.
1974: *
1975: * @return A collection of markers (possibly <code>null</code>).
1976: */
1977: public Collection getDomainMarkers(int index, Layer layer) {
1978: Collection result = null;
1979: Integer key = new Integer(index);
1980: if (layer == Layer.FOREGROUND) {
1981: result = (Collection) this .foregroundDomainMarkers.get(key);
1982: } else if (layer == Layer.BACKGROUND) {
1983: result = (Collection) this .backgroundDomainMarkers.get(key);
1984: }
1985: if (result != null) {
1986: result = Collections.unmodifiableCollection(result);
1987: }
1988: return result;
1989: }
1990:
1991: /**
1992: * Clears all the domain markers for the specified renderer.
1993: *
1994: * @param index the renderer index.
1995: *
1996: * @see #clearRangeMarkers(int)
1997: */
1998: public void clearDomainMarkers(int index) {
1999: Integer key = new Integer(index);
2000: if (this .backgroundDomainMarkers != null) {
2001: Collection markers = (Collection) this .backgroundDomainMarkers
2002: .get(key);
2003: if (markers != null) {
2004: Iterator iterator = markers.iterator();
2005: while (iterator.hasNext()) {
2006: Marker m = (Marker) iterator.next();
2007: m.removeChangeListener(this );
2008: }
2009: markers.clear();
2010: }
2011: }
2012: if (this .foregroundDomainMarkers != null) {
2013: Collection markers = (Collection) this .foregroundDomainMarkers
2014: .get(key);
2015: if (markers != null) {
2016: Iterator iterator = markers.iterator();
2017: while (iterator.hasNext()) {
2018: Marker m = (Marker) iterator.next();
2019: m.removeChangeListener(this );
2020: }
2021: markers.clear();
2022: }
2023: }
2024: notifyListeners(new PlotChangeEvent(this ));
2025: }
2026:
2027: /**
2028: * Adds a marker for display (in the foreground) against the range axis and
2029: * sends a {@link PlotChangeEvent} to all registered listeners. Typically a
2030: * marker will be drawn by the renderer as a line perpendicular to the
2031: * range axis, however this is entirely up to the renderer.
2032: *
2033: * @param marker the marker (<code>null</code> not permitted).
2034: */
2035: public void addRangeMarker(Marker marker) {
2036: addRangeMarker(marker, Layer.FOREGROUND);
2037: }
2038:
2039: /**
2040: * Adds a marker for display against the range axis and sends a
2041: * {@link PlotChangeEvent} to all registered listeners. Typically a marker
2042: * will be drawn by the renderer as a line perpendicular to the range axis,
2043: * however this is entirely up to the renderer.
2044: *
2045: * @param marker the marker (<code>null</code> not permitted).
2046: * @param layer the layer (foreground or background) (<code>null</code>
2047: * not permitted).
2048: */
2049: public void addRangeMarker(Marker marker, Layer layer) {
2050: addRangeMarker(0, marker, layer);
2051: }
2052:
2053: /**
2054: * Adds a marker for display by a particular renderer.
2055: * <P>
2056: * Typically a marker will be drawn by the renderer as a line perpendicular
2057: * to a range axis, however this is entirely up to the renderer.
2058: *
2059: * @param index the renderer index.
2060: * @param marker the marker.
2061: * @param layer the layer.
2062: */
2063: public void addRangeMarker(int index, Marker marker, Layer layer) {
2064: Collection markers;
2065: if (layer == Layer.FOREGROUND) {
2066: markers = (Collection) this .foregroundRangeMarkers
2067: .get(new Integer(index));
2068: if (markers == null) {
2069: markers = new java.util.ArrayList();
2070: this .foregroundRangeMarkers.put(new Integer(index),
2071: markers);
2072: }
2073: markers.add(marker);
2074: } else if (layer == Layer.BACKGROUND) {
2075: markers = (Collection) this .backgroundRangeMarkers
2076: .get(new Integer(index));
2077: if (markers == null) {
2078: markers = new java.util.ArrayList();
2079: this .backgroundRangeMarkers.put(new Integer(index),
2080: markers);
2081: }
2082: markers.add(marker);
2083: }
2084: marker.addChangeListener(this );
2085: notifyListeners(new PlotChangeEvent(this ));
2086: }
2087:
2088: /**
2089: * Clears all the range markers for the plot and sends a
2090: * {@link PlotChangeEvent} to all registered listeners.
2091: *
2092: * @see #clearDomainMarkers()
2093: */
2094: public void clearRangeMarkers() {
2095: if (this .backgroundRangeMarkers != null) {
2096: Set keys = this .backgroundRangeMarkers.keySet();
2097: Iterator iterator = keys.iterator();
2098: while (iterator.hasNext()) {
2099: Integer key = (Integer) iterator.next();
2100: clearRangeMarkers(key.intValue());
2101: }
2102: this .backgroundRangeMarkers.clear();
2103: }
2104: if (this .foregroundRangeMarkers != null) {
2105: Set keys = this .foregroundRangeMarkers.keySet();
2106: Iterator iterator = keys.iterator();
2107: while (iterator.hasNext()) {
2108: Integer key = (Integer) iterator.next();
2109: clearRangeMarkers(key.intValue());
2110: }
2111: this .foregroundRangeMarkers.clear();
2112: }
2113: notifyListeners(new PlotChangeEvent(this ));
2114: }
2115:
2116: /**
2117: * Returns the list of range markers (read only) for the specified layer.
2118: *
2119: * @param layer the layer (foreground or background).
2120: *
2121: * @return The list of range markers.
2122: *
2123: * @see #getRangeMarkers(int, Layer)
2124: */
2125: public Collection getRangeMarkers(Layer layer) {
2126: return getRangeMarkers(0, layer);
2127: }
2128:
2129: /**
2130: * Returns a collection of range markers for a particular renderer and
2131: * layer.
2132: *
2133: * @param index the renderer index.
2134: * @param layer the layer.
2135: *
2136: * @return A collection of markers (possibly <code>null</code>).
2137: */
2138: public Collection getRangeMarkers(int index, Layer layer) {
2139: Collection result = null;
2140: Integer key = new Integer(index);
2141: if (layer == Layer.FOREGROUND) {
2142: result = (Collection) this .foregroundRangeMarkers.get(key);
2143: } else if (layer == Layer.BACKGROUND) {
2144: result = (Collection) this .backgroundRangeMarkers.get(key);
2145: }
2146: if (result != null) {
2147: result = Collections.unmodifiableCollection(result);
2148: }
2149: return result;
2150: }
2151:
2152: /**
2153: * Clears all the range markers for the specified renderer.
2154: *
2155: * @param index the renderer index.
2156: *
2157: * @see #clearDomainMarkers(int)
2158: */
2159: public void clearRangeMarkers(int index) {
2160: Integer key = new Integer(index);
2161: if (this .backgroundRangeMarkers != null) {
2162: Collection markers = (Collection) this .backgroundRangeMarkers
2163: .get(key);
2164: if (markers != null) {
2165: Iterator iterator = markers.iterator();
2166: while (iterator.hasNext()) {
2167: Marker m = (Marker) iterator.next();
2168: m.removeChangeListener(this );
2169: }
2170: markers.clear();
2171: }
2172: }
2173: if (this .foregroundRangeMarkers != null) {
2174: Collection markers = (Collection) this .foregroundRangeMarkers
2175: .get(key);
2176: if (markers != null) {
2177: Iterator iterator = markers.iterator();
2178: while (iterator.hasNext()) {
2179: Marker m = (Marker) iterator.next();
2180: m.removeChangeListener(this );
2181: }
2182: markers.clear();
2183: }
2184: }
2185: notifyListeners(new PlotChangeEvent(this ));
2186: }
2187:
2188: /**
2189: * Returns a flag indicating whether or not the range crosshair is visible.
2190: *
2191: * @return The flag.
2192: *
2193: * @see #setRangeCrosshairVisible(boolean)
2194: */
2195: public boolean isRangeCrosshairVisible() {
2196: return this .rangeCrosshairVisible;
2197: }
2198:
2199: /**
2200: * Sets the flag indicating whether or not the range crosshair is visible.
2201: *
2202: * @param flag the new value of the flag.
2203: *
2204: * @see #isRangeCrosshairVisible()
2205: */
2206: public void setRangeCrosshairVisible(boolean flag) {
2207: if (this .rangeCrosshairVisible != flag) {
2208: this .rangeCrosshairVisible = flag;
2209: notifyListeners(new PlotChangeEvent(this ));
2210: }
2211: }
2212:
2213: /**
2214: * Returns a flag indicating whether or not the crosshair should "lock-on"
2215: * to actual data values.
2216: *
2217: * @return The flag.
2218: *
2219: * @see #setRangeCrosshairLockedOnData(boolean)
2220: */
2221: public boolean isRangeCrosshairLockedOnData() {
2222: return this .rangeCrosshairLockedOnData;
2223: }
2224:
2225: /**
2226: * Sets the flag indicating whether or not the range crosshair should
2227: * "lock-on" to actual data values.
2228: *
2229: * @param flag the flag.
2230: *
2231: * @see #isRangeCrosshairLockedOnData()
2232: */
2233: public void setRangeCrosshairLockedOnData(boolean flag) {
2234:
2235: if (this .rangeCrosshairLockedOnData != flag) {
2236: this .rangeCrosshairLockedOnData = flag;
2237: notifyListeners(new PlotChangeEvent(this ));
2238: }
2239:
2240: }
2241:
2242: /**
2243: * Returns the range crosshair value.
2244: *
2245: * @return The value.
2246: *
2247: * @see #setRangeCrosshairValue(double)
2248: */
2249: public double getRangeCrosshairValue() {
2250: return this .rangeCrosshairValue;
2251: }
2252:
2253: /**
2254: * Sets the domain crosshair value.
2255: * <P>
2256: * Registered listeners are notified that the plot has been modified, but
2257: * only if the crosshair is visible.
2258: *
2259: * @param value the new value.
2260: *
2261: * @see #getRangeCrosshairValue()
2262: */
2263: public void setRangeCrosshairValue(double value) {
2264: setRangeCrosshairValue(value, true);
2265: }
2266:
2267: /**
2268: * Sets the range crosshair value and, if requested, sends a
2269: * {@link PlotChangeEvent} to all registered listeners (but only if the
2270: * crosshair is visible).
2271: *
2272: * @param value the new value.
2273: * @param notify a flag that controls whether or not listeners are
2274: * notified.
2275: *
2276: * @see #getRangeCrosshairValue()
2277: */
2278: public void setRangeCrosshairValue(double value, boolean notify) {
2279: this .rangeCrosshairValue = value;
2280: if (isRangeCrosshairVisible() && notify) {
2281: notifyListeners(new PlotChangeEvent(this ));
2282: }
2283: }
2284:
2285: /**
2286: * Returns the pen-style (<code>Stroke</code>) used to draw the crosshair
2287: * (if visible).
2288: *
2289: * @return The crosshair stroke (never <code>null</code>).
2290: *
2291: * @see #setRangeCrosshairStroke(Stroke)
2292: * @see #isRangeCrosshairVisible()
2293: * @see #getRangeCrosshairPaint()
2294: */
2295: public Stroke getRangeCrosshairStroke() {
2296: return this .rangeCrosshairStroke;
2297: }
2298:
2299: /**
2300: * Sets the pen-style (<code>Stroke</code>) used to draw the range
2301: * crosshair (if visible), and sends a {@link PlotChangeEvent} to all
2302: * registered listeners.
2303: *
2304: * @param stroke the new crosshair stroke (<code>null</code> not
2305: * permitted).
2306: *
2307: * @see #getRangeCrosshairStroke()
2308: */
2309: public void setRangeCrosshairStroke(Stroke stroke) {
2310: if (stroke == null) {
2311: throw new IllegalArgumentException(
2312: "Null 'stroke' argument.");
2313: }
2314: this .rangeCrosshairStroke = stroke;
2315: notifyListeners(new PlotChangeEvent(this ));
2316: }
2317:
2318: /**
2319: * Returns the paint used to draw the range crosshair.
2320: *
2321: * @return The paint (never <code>null</code>).
2322: *
2323: * @see #setRangeCrosshairPaint(Paint)
2324: * @see #isRangeCrosshairVisible()
2325: * @see #getRangeCrosshairStroke()
2326: */
2327: public Paint getRangeCrosshairPaint() {
2328: return this .rangeCrosshairPaint;
2329: }
2330:
2331: /**
2332: * Sets the paint used to draw the range crosshair (if visible) and
2333: * sends a {@link PlotChangeEvent} to all registered listeners.
2334: *
2335: * @param paint the paint (<code>null</code> not permitted).
2336: *
2337: * @see #getRangeCrosshairPaint()
2338: */
2339: public void setRangeCrosshairPaint(Paint paint) {
2340: if (paint == null) {
2341: throw new IllegalArgumentException("Null 'paint' argument.");
2342: }
2343: this .rangeCrosshairPaint = paint;
2344: notifyListeners(new PlotChangeEvent(this ));
2345: }
2346:
2347: /**
2348: * Returns the list of annotations.
2349: *
2350: * @return The list of annotations.
2351: */
2352: public List getAnnotations() {
2353: return this .annotations;
2354: }
2355:
2356: /**
2357: * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all
2358: * registered listeners.
2359: *
2360: * @param annotation the annotation (<code>null</code> not permitted).
2361: *
2362: * @see #removeAnnotation(CategoryAnnotation)
2363: */
2364: public void addAnnotation(CategoryAnnotation annotation) {
2365: if (annotation == null) {
2366: throw new IllegalArgumentException(
2367: "Null 'annotation' argument.");
2368: }
2369: this .annotations.add(annotation);
2370: notifyListeners(new PlotChangeEvent(this ));
2371: }
2372:
2373: /**
2374: * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2375: * to all registered listeners.
2376: *
2377: * @param annotation the annotation (<code>null</code> not permitted).
2378: *
2379: * @return A boolean (indicates whether or not the annotation was removed).
2380: *
2381: * @see #addAnnotation(CategoryAnnotation)
2382: */
2383: public boolean removeAnnotation(CategoryAnnotation annotation) {
2384: if (annotation == null) {
2385: throw new IllegalArgumentException(
2386: "Null 'annotation' argument.");
2387: }
2388: boolean removed = this .annotations.remove(annotation);
2389: if (removed) {
2390: notifyListeners(new PlotChangeEvent(this ));
2391: }
2392: return removed;
2393: }
2394:
2395: /**
2396: * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2397: * registered listeners.
2398: */
2399: public void clearAnnotations() {
2400: this .annotations.clear();
2401: notifyListeners(new PlotChangeEvent(this ));
2402: }
2403:
2404: /**
2405: * Calculates the space required for the domain axis/axes.
2406: *
2407: * @param g2 the graphics device.
2408: * @param plotArea the plot area.
2409: * @param space a carrier for the result (<code>null</code> permitted).
2410: *
2411: * @return The required space.
2412: */
2413: protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
2414: Rectangle2D plotArea, AxisSpace space) {
2415:
2416: if (space == null) {
2417: space = new AxisSpace();
2418: }
2419:
2420: // reserve some space for the domain axis...
2421: if (this .fixedDomainAxisSpace != null) {
2422: if (this .orientation == PlotOrientation.HORIZONTAL) {
2423: space.ensureAtLeast(
2424: this .fixedDomainAxisSpace.getLeft(),
2425: RectangleEdge.LEFT);
2426: space.ensureAtLeast(this .fixedDomainAxisSpace
2427: .getRight(), RectangleEdge.RIGHT);
2428: } else if (this .orientation == PlotOrientation.VERTICAL) {
2429: space.ensureAtLeast(this .fixedDomainAxisSpace.getTop(),
2430: RectangleEdge.TOP);
2431: space.ensureAtLeast(this .fixedDomainAxisSpace
2432: .getBottom(), RectangleEdge.BOTTOM);
2433: }
2434: } else {
2435: // reserve space for the primary domain axis...
2436: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
2437: getDomainAxisLocation(), this .orientation);
2438: if (this .drawSharedDomainAxis) {
2439: space = getDomainAxis().reserveSpace(g2, this ,
2440: plotArea, domainEdge, space);
2441: }
2442:
2443: // reserve space for any domain axes...
2444: for (int i = 0; i < this .domainAxes.size(); i++) {
2445: Axis xAxis = (Axis) this .domainAxes.get(i);
2446: if (xAxis != null) {
2447: RectangleEdge edge = getDomainAxisEdge(i);
2448: space = xAxis.reserveSpace(g2, this , plotArea,
2449: edge, space);
2450: }
2451: }
2452: }
2453:
2454: return space;
2455:
2456: }
2457:
2458: /**
2459: * Calculates the space required for the range axis/axes.
2460: *
2461: * @param g2 the graphics device.
2462: * @param plotArea the plot area.
2463: * @param space a carrier for the result (<code>null</code> permitted).
2464: *
2465: * @return The required space.
2466: */
2467: protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
2468: Rectangle2D plotArea, AxisSpace space) {
2469:
2470: if (space == null) {
2471: space = new AxisSpace();
2472: }
2473:
2474: // reserve some space for the range axis...
2475: if (this .fixedRangeAxisSpace != null) {
2476: if (this .orientation == PlotOrientation.HORIZONTAL) {
2477: space.ensureAtLeast(this .fixedRangeAxisSpace.getTop(),
2478: RectangleEdge.TOP);
2479: space.ensureAtLeast(this .fixedRangeAxisSpace
2480: .getBottom(), RectangleEdge.BOTTOM);
2481: } else if (this .orientation == PlotOrientation.VERTICAL) {
2482: space.ensureAtLeast(this .fixedRangeAxisSpace.getLeft(),
2483: RectangleEdge.LEFT);
2484: space.ensureAtLeast(
2485: this .fixedRangeAxisSpace.getRight(),
2486: RectangleEdge.RIGHT);
2487: }
2488: } else {
2489: // reserve space for the range axes (if any)...
2490: for (int i = 0; i < this .rangeAxes.size(); i++) {
2491: Axis yAxis = (Axis) this .rangeAxes.get(i);
2492: if (yAxis != null) {
2493: RectangleEdge edge = getRangeAxisEdge(i);
2494: space = yAxis.reserveSpace(g2, this , plotArea,
2495: edge, space);
2496: }
2497: }
2498: }
2499: return space;
2500:
2501: }
2502:
2503: /**
2504: * Calculates the space required for the axes.
2505: *
2506: * @param g2 the graphics device.
2507: * @param plotArea the plot area.
2508: *
2509: * @return The space required for the axes.
2510: */
2511: protected AxisSpace calculateAxisSpace(Graphics2D g2,
2512: Rectangle2D plotArea) {
2513: AxisSpace space = new AxisSpace();
2514: space = calculateRangeAxisSpace(g2, plotArea, space);
2515: space = calculateDomainAxisSpace(g2, plotArea, space);
2516: return space;
2517: }
2518:
2519: /**
2520: * Draws the plot on a Java 2D graphics device (such as the screen or a
2521: * printer).
2522: * <P>
2523: * At your option, you may supply an instance of {@link PlotRenderingInfo}.
2524: * If you do, it will be populated with information about the drawing,
2525: * including various plot dimensions and tooltip info.
2526: *
2527: * @param g2 the graphics device.
2528: * @param area the area within which the plot (including axes) should
2529: * be drawn.
2530: * @param anchor the anchor point (<code>null</code> permitted).
2531: * @param parentState the state from the parent plot, if there is one.
2532: * @param state collects info as the chart is drawn (possibly
2533: * <code>null</code>).
2534: */
2535: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2536: PlotState parentState, PlotRenderingInfo state) {
2537:
2538: // if the plot area is too small, just return...
2539: boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2540: boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2541: if (b1 || b2) {
2542: return;
2543: }
2544:
2545: // record the plot area...
2546: if (state == null) {
2547: // if the incoming state is null, no information will be passed
2548: // back to the caller - but we create a temporary state to record
2549: // the plot area, since that is used later by the axes
2550: state = new PlotRenderingInfo(null);
2551: }
2552: state.setPlotArea(area);
2553:
2554: // adjust the drawing area for the plot insets (if any)...
2555: RectangleInsets insets = getInsets();
2556: insets.trim(area);
2557:
2558: // calculate the data area...
2559: AxisSpace space = calculateAxisSpace(g2, area);
2560: Rectangle2D dataArea = space.shrink(area, null);
2561: this .axisOffset.trim(dataArea);
2562:
2563: state.setDataArea(dataArea);
2564:
2565: // if there is a renderer, it draws the background, otherwise use the
2566: // default background...
2567: if (getRenderer() != null) {
2568: getRenderer().drawBackground(g2, this , dataArea);
2569: } else {
2570: drawBackground(g2, dataArea);
2571: }
2572:
2573: Map axisStateMap = drawAxes(g2, area, dataArea, state);
2574:
2575: // don't let anyone draw outside the data area
2576: Shape savedClip = g2.getClip();
2577: g2.clip(dataArea);
2578:
2579: drawDomainGridlines(g2, dataArea);
2580:
2581: AxisState rangeAxisState = (AxisState) axisStateMap
2582: .get(getRangeAxis());
2583: if (rangeAxisState == null) {
2584: if (parentState != null) {
2585: rangeAxisState = (AxisState) parentState
2586: .getSharedAxisStates().get(getRangeAxis());
2587: }
2588: }
2589: if (rangeAxisState != null) {
2590: drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2591: }
2592:
2593: // draw the markers...
2594: for (int i = 0; i < this .renderers.size(); i++) {
2595: drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2596: }
2597: for (int i = 0; i < this .renderers.size(); i++) {
2598: drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2599: }
2600:
2601: // now render data items...
2602: boolean foundData = false;
2603:
2604: // set up the alpha-transparency...
2605: Composite originalComposite = g2.getComposite();
2606: g2.setComposite(AlphaComposite.getInstance(
2607: AlphaComposite.SRC_OVER, getForegroundAlpha()));
2608:
2609: DatasetRenderingOrder order = getDatasetRenderingOrder();
2610: if (order == DatasetRenderingOrder.FORWARD) {
2611: for (int i = 0; i < this .datasets.size(); i++) {
2612: foundData = render(g2, dataArea, i, state) || foundData;
2613: }
2614: } else { // DatasetRenderingOrder.REVERSE
2615: for (int i = this .datasets.size() - 1; i >= 0; i--) {
2616: foundData = render(g2, dataArea, i, state) || foundData;
2617: }
2618: }
2619: // draw the foreground markers...
2620: for (int i = 0; i < this .renderers.size(); i++) {
2621: drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2622: }
2623: for (int i = 0; i < this .renderers.size(); i++) {
2624: drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2625: }
2626:
2627: // draw the annotations (if any)...
2628: drawAnnotations(g2, dataArea);
2629:
2630: g2.setClip(savedClip);
2631: g2.setComposite(originalComposite);
2632:
2633: if (!foundData) {
2634: drawNoDataMessage(g2, dataArea);
2635: }
2636:
2637: // draw range crosshair if required...
2638: if (isRangeCrosshairVisible()) {
2639: // FIXME: this doesn't handle multiple range axes
2640: drawRangeCrosshair(g2, dataArea, getOrientation(),
2641: getRangeCrosshairValue(), getRangeAxis(),
2642: getRangeCrosshairStroke(), getRangeCrosshairPaint());
2643: }
2644:
2645: // draw an outline around the plot area...
2646: if (getRenderer() != null) {
2647: getRenderer().drawOutline(g2, this , dataArea);
2648: } else {
2649: drawOutline(g2, dataArea);
2650: }
2651:
2652: }
2653:
2654: /**
2655: * Draws the plot background (the background color and/or image).
2656: * <P>
2657: * This method will be called during the chart drawing process and is
2658: * declared public so that it can be accessed by the renderers used by
2659: * certain subclasses. You shouldn't need to call this method directly.
2660: *
2661: * @param g2 the graphics device.
2662: * @param area the area within which the plot should be drawn.
2663: */
2664: public void drawBackground(Graphics2D g2, Rectangle2D area) {
2665: fillBackground(g2, area, this .orientation);
2666: drawBackgroundImage(g2, area);
2667: }
2668:
2669: /**
2670: * A utility method for drawing the plot's axes.
2671: *
2672: * @param g2 the graphics device.
2673: * @param plotArea the plot area.
2674: * @param dataArea the data area.
2675: * @param plotState collects information about the plot (<code>null</code>
2676: * permitted).
2677: *
2678: * @return A map containing the axis states.
2679: */
2680: protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea,
2681: Rectangle2D dataArea, PlotRenderingInfo plotState) {
2682:
2683: AxisCollection axisCollection = new AxisCollection();
2684:
2685: // add domain axes to lists...
2686: for (int index = 0; index < this .domainAxes.size(); index++) {
2687: CategoryAxis xAxis = (CategoryAxis) this .domainAxes
2688: .get(index);
2689: if (xAxis != null) {
2690: axisCollection.add(xAxis, getDomainAxisEdge(index));
2691: }
2692: }
2693:
2694: // add range axes to lists...
2695: for (int index = 0; index < this .rangeAxes.size(); index++) {
2696: ValueAxis yAxis = (ValueAxis) this .rangeAxes.get(index);
2697: if (yAxis != null) {
2698: axisCollection.add(yAxis, getRangeAxisEdge(index));
2699: }
2700: }
2701:
2702: Map axisStateMap = new HashMap();
2703:
2704: // draw the top axes
2705: double cursor = dataArea.getMinY()
2706: - this .axisOffset.calculateTopOutset(dataArea
2707: .getHeight());
2708: Iterator iterator = axisCollection.getAxesAtTop().iterator();
2709: while (iterator.hasNext()) {
2710: Axis axis = (Axis) iterator.next();
2711: if (axis != null) {
2712: AxisState axisState = axis.draw(g2, cursor, plotArea,
2713: dataArea, RectangleEdge.TOP, plotState);
2714: cursor = axisState.getCursor();
2715: axisStateMap.put(axis, axisState);
2716: }
2717: }
2718:
2719: // draw the bottom axes
2720: cursor = dataArea.getMaxY()
2721: + this .axisOffset.calculateBottomOutset(dataArea
2722: .getHeight());
2723: iterator = axisCollection.getAxesAtBottom().iterator();
2724: while (iterator.hasNext()) {
2725: Axis axis = (Axis) iterator.next();
2726: if (axis != null) {
2727: AxisState axisState = axis.draw(g2, cursor, plotArea,
2728: dataArea, RectangleEdge.BOTTOM, plotState);
2729: cursor = axisState.getCursor();
2730: axisStateMap.put(axis, axisState);
2731: }
2732: }
2733:
2734: // draw the left axes
2735: cursor = dataArea.getMinX()
2736: - this .axisOffset.calculateLeftOutset(dataArea
2737: .getWidth());
2738: iterator = axisCollection.getAxesAtLeft().iterator();
2739: while (iterator.hasNext()) {
2740: Axis axis = (Axis) iterator.next();
2741: if (axis != null) {
2742: AxisState axisState = axis.draw(g2, cursor, plotArea,
2743: dataArea, RectangleEdge.LEFT, plotState);
2744: cursor = axisState.getCursor();
2745: axisStateMap.put(axis, axisState);
2746: }
2747: }
2748:
2749: // draw the right axes
2750: cursor = dataArea.getMaxX()
2751: + this .axisOffset.calculateRightOutset(dataArea
2752: .getWidth());
2753: iterator = axisCollection.getAxesAtRight().iterator();
2754: while (iterator.hasNext()) {
2755: Axis axis = (Axis) iterator.next();
2756: if (axis != null) {
2757: AxisState axisState = axis.draw(g2, cursor, plotArea,
2758: dataArea, RectangleEdge.RIGHT, plotState);
2759: cursor = axisState.getCursor();
2760: axisStateMap.put(axis, axisState);
2761: }
2762: }
2763:
2764: return axisStateMap;
2765:
2766: }
2767:
2768: /**
2769: * Draws a representation of a dataset within the dataArea region using the
2770: * appropriate renderer.
2771: *
2772: * @param g2 the graphics device.
2773: * @param dataArea the region in which the data is to be drawn.
2774: * @param index the dataset and renderer index.
2775: * @param info an optional object for collection dimension information.
2776: *
2777: * @return A boolean that indicates whether or not real data was found.
2778: */
2779: public boolean render(Graphics2D g2, Rectangle2D dataArea,
2780: int index, PlotRenderingInfo info) {
2781:
2782: boolean foundData = false;
2783: CategoryDataset currentDataset = getDataset(index);
2784: CategoryItemRenderer renderer = getRenderer(index);
2785: CategoryAxis domainAxis = getDomainAxisForDataset(index);
2786: ValueAxis rangeAxis = getRangeAxisForDataset(index);
2787: boolean hasData = !DatasetUtilities
2788: .isEmptyOrNull(currentDataset);
2789: if (hasData && renderer != null) {
2790:
2791: foundData = true;
2792: CategoryItemRendererState state = renderer.initialise(g2,
2793: dataArea, this , index, info);
2794: int columnCount = currentDataset.getColumnCount();
2795: int rowCount = currentDataset.getRowCount();
2796: int passCount = renderer.getPassCount();
2797: for (int pass = 0; pass < passCount; pass++) {
2798: if (this .columnRenderingOrder == SortOrder.ASCENDING) {
2799: for (int column = 0; column < columnCount; column++) {
2800: if (this .rowRenderingOrder == SortOrder.ASCENDING) {
2801: for (int row = 0; row < rowCount; row++) {
2802: renderer.drawItem(g2, state, dataArea,
2803: this , domainAxis, rangeAxis,
2804: currentDataset, row, column,
2805: pass);
2806: }
2807: } else {
2808: for (int row = rowCount - 1; row >= 0; row--) {
2809: renderer.drawItem(g2, state, dataArea,
2810: this , domainAxis, rangeAxis,
2811: currentDataset, row, column,
2812: pass);
2813: }
2814: }
2815: }
2816: } else {
2817: for (int column = columnCount - 1; column >= 0; column--) {
2818: if (this .rowRenderingOrder == SortOrder.ASCENDING) {
2819: for (int row = 0; row < rowCount; row++) {
2820: renderer.drawItem(g2, state, dataArea,
2821: this , domainAxis, rangeAxis,
2822: currentDataset, row, column,
2823: pass);
2824: }
2825: } else {
2826: for (int row = rowCount - 1; row >= 0; row--) {
2827: renderer.drawItem(g2, state, dataArea,
2828: this , domainAxis, rangeAxis,
2829: currentDataset, row, column,
2830: pass);
2831: }
2832: }
2833: }
2834: }
2835: }
2836: }
2837: return foundData;
2838:
2839: }
2840:
2841: /**
2842: * Draws the gridlines for the plot.
2843: *
2844: * @param g2 the graphics device.
2845: * @param dataArea the area inside the axes.
2846: *
2847: * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List)
2848: */
2849: protected void drawDomainGridlines(Graphics2D g2,
2850: Rectangle2D dataArea) {
2851:
2852: // draw the domain grid lines, if any...
2853: if (isDomainGridlinesVisible()) {
2854: CategoryAnchor anchor = getDomainGridlinePosition();
2855: RectangleEdge domainAxisEdge = getDomainAxisEdge();
2856: Stroke gridStroke = getDomainGridlineStroke();
2857: Paint gridPaint = getDomainGridlinePaint();
2858: if ((gridStroke != null) && (gridPaint != null)) {
2859: // iterate over the categories
2860: CategoryDataset data = getDataset();
2861: if (data != null) {
2862: CategoryAxis axis = getDomainAxis();
2863: if (axis != null) {
2864: int columnCount = data.getColumnCount();
2865: for (int c = 0; c < columnCount; c++) {
2866: double xx = axis
2867: .getCategoryJava2DCoordinate(
2868: anchor, c, columnCount,
2869: dataArea, domainAxisEdge);
2870: CategoryItemRenderer renderer1 = getRenderer();
2871: if (renderer1 != null) {
2872: renderer1.drawDomainGridline(g2, this ,
2873: dataArea, xx);
2874: }
2875: }
2876: }
2877: }
2878: }
2879: }
2880: }
2881:
2882: /**
2883: * Draws the gridlines for the plot.
2884: *
2885: * @param g2 the graphics device.
2886: * @param dataArea the area inside the axes.
2887: * @param ticks the ticks.
2888: *
2889: * @see #drawDomainGridlines(Graphics2D, Rectangle2D)
2890: */
2891: protected void drawRangeGridlines(Graphics2D g2,
2892: Rectangle2D dataArea, List ticks) {
2893: // draw the range grid lines, if any...
2894: if (isRangeGridlinesVisible()) {
2895: Stroke gridStroke = getRangeGridlineStroke();
2896: Paint gridPaint = getRangeGridlinePaint();
2897: if ((gridStroke != null) && (gridPaint != null)) {
2898: ValueAxis axis = getRangeAxis();
2899: if (axis != null) {
2900: Iterator iterator = ticks.iterator();
2901: while (iterator.hasNext()) {
2902: ValueTick tick = (ValueTick) iterator.next();
2903: CategoryItemRenderer renderer1 = getRenderer();
2904: if (renderer1 != null) {
2905: renderer1.drawRangeGridline(g2, this ,
2906: getRangeAxis(), dataArea, tick
2907: .getValue());
2908: }
2909: }
2910: }
2911: }
2912: }
2913: }
2914:
2915: /**
2916: * Draws the annotations...
2917: *
2918: * @param g2 the graphics device.
2919: * @param dataArea the data area.
2920: */
2921: protected void drawAnnotations(Graphics2D g2, Rectangle2D dataArea) {
2922:
2923: if (getAnnotations() != null) {
2924: Iterator iterator = getAnnotations().iterator();
2925: while (iterator.hasNext()) {
2926: CategoryAnnotation annotation = (CategoryAnnotation) iterator
2927: .next();
2928: annotation.draw(g2, this , dataArea, getDomainAxis(),
2929: getRangeAxis());
2930: }
2931: }
2932:
2933: }
2934:
2935: /**
2936: * Draws the domain markers (if any) for an axis and layer. This method is
2937: * typically called from within the draw() method.
2938: *
2939: * @param g2 the graphics device.
2940: * @param dataArea the data area.
2941: * @param index the renderer index.
2942: * @param layer the layer (foreground or background).
2943: *
2944: * @see #drawRangeMarkers(Graphics2D, Rectangle2D, int, Layer)
2945: */
2946: protected void drawDomainMarkers(Graphics2D g2,
2947: Rectangle2D dataArea, int index, Layer layer) {
2948:
2949: CategoryItemRenderer r = getRenderer(index);
2950: if (r == null) {
2951: return;
2952: }
2953:
2954: Collection markers = getDomainMarkers(index, layer);
2955: CategoryAxis axis = getDomainAxisForDataset(index);
2956: if (markers != null && axis != null) {
2957: Iterator iterator = markers.iterator();
2958: while (iterator.hasNext()) {
2959: CategoryMarker marker = (CategoryMarker) iterator
2960: .next();
2961: r.drawDomainMarker(g2, this , axis, marker, dataArea);
2962: }
2963: }
2964:
2965: }
2966:
2967: /**
2968: * Draws the range markers (if any) for an axis and layer. This method is
2969: * typically called from within the draw() method.
2970: *
2971: * @param g2 the graphics device.
2972: * @param dataArea the data area.
2973: * @param index the renderer index.
2974: * @param layer the layer (foreground or background).
2975: *
2976: * @see #drawDomainMarkers(Graphics2D, Rectangle2D, int, Layer)
2977: */
2978: protected void drawRangeMarkers(Graphics2D g2,
2979: Rectangle2D dataArea, int index, Layer layer) {
2980:
2981: CategoryItemRenderer r = getRenderer(index);
2982: if (r == null) {
2983: return;
2984: }
2985:
2986: Collection markers = getRangeMarkers(index, layer);
2987: ValueAxis axis = getRangeAxisForDataset(index);
2988: if (markers != null && axis != null) {
2989: Iterator iterator = markers.iterator();
2990: while (iterator.hasNext()) {
2991: Marker marker = (Marker) iterator.next();
2992: r.drawRangeMarker(g2, this , axis, marker, dataArea);
2993: }
2994: }
2995:
2996: }
2997:
2998: /**
2999: * Utility method for drawing a line perpendicular to the range axis (used
3000: * for crosshairs).
3001: *
3002: * @param g2 the graphics device.
3003: * @param dataArea the area defined by the axes.
3004: * @param value the data value.
3005: * @param stroke the line stroke (<code>null</code> not permitted).
3006: * @param paint the line paint (<code>null</code> not permitted).
3007: */
3008: protected void drawRangeLine(Graphics2D g2, Rectangle2D dataArea,
3009: double value, Stroke stroke, Paint paint) {
3010:
3011: double java2D = getRangeAxis().valueToJava2D(value, dataArea,
3012: getRangeAxisEdge());
3013: Line2D line = null;
3014: if (this .orientation == PlotOrientation.HORIZONTAL) {
3015: line = new Line2D.Double(java2D, dataArea.getMinY(),
3016: java2D, dataArea.getMaxY());
3017: } else if (this .orientation == PlotOrientation.VERTICAL) {
3018: line = new Line2D.Double(dataArea.getMinX(), java2D,
3019: dataArea.getMaxX(), java2D);
3020: }
3021: g2.setStroke(stroke);
3022: g2.setPaint(paint);
3023: g2.draw(line);
3024:
3025: }
3026:
3027: /**
3028: * Draws a range crosshair.
3029: *
3030: * @param g2 the graphics target.
3031: * @param dataArea the data area.
3032: * @param orientation the plot orientation.
3033: * @param value the crosshair value.
3034: * @param axis the axis against which the value is measured.
3035: * @param stroke the stroke used to draw the crosshair line.
3036: * @param paint the paint used to draw the crosshair line.
3037: *
3038: * @since 1.0.5
3039: */
3040: protected void drawRangeCrosshair(Graphics2D g2,
3041: Rectangle2D dataArea, PlotOrientation orientation,
3042: double value, ValueAxis axis, Stroke stroke, Paint paint) {
3043:
3044: if (!axis.getRange().contains(value)) {
3045: return;
3046: }
3047: Line2D line = null;
3048: if (orientation == PlotOrientation.HORIZONTAL) {
3049: double xx = axis.valueToJava2D(value, dataArea,
3050: RectangleEdge.BOTTOM);
3051: line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3052: dataArea.getMaxY());
3053: } else {
3054: double yy = axis.valueToJava2D(value, dataArea,
3055: RectangleEdge.LEFT);
3056: line = new Line2D.Double(dataArea.getMinX(), yy, dataArea
3057: .getMaxX(), yy);
3058: }
3059: g2.setStroke(stroke);
3060: g2.setPaint(paint);
3061: g2.draw(line);
3062:
3063: }
3064:
3065: /**
3066: * Returns the range of data values that will be plotted against the range
3067: * axis. If the dataset is <code>null</code>, this method returns
3068: * <code>null</code>.
3069: *
3070: * @param axis the axis.
3071: *
3072: * @return The data range.
3073: */
3074: public Range getDataRange(ValueAxis axis) {
3075:
3076: Range result = null;
3077: List mappedDatasets = new ArrayList();
3078:
3079: int rangeIndex = this .rangeAxes.indexOf(axis);
3080: if (rangeIndex >= 0) {
3081: mappedDatasets
3082: .addAll(datasetsMappedToRangeAxis(rangeIndex));
3083: } else if (axis == getRangeAxis()) {
3084: mappedDatasets.addAll(datasetsMappedToRangeAxis(0));
3085: }
3086:
3087: // iterate through the datasets that map to the axis and get the union
3088: // of the ranges.
3089: Iterator iterator = mappedDatasets.iterator();
3090: while (iterator.hasNext()) {
3091: CategoryDataset d = (CategoryDataset) iterator.next();
3092: CategoryItemRenderer r = getRendererForDataset(d);
3093: if (r != null) {
3094: result = Range.combine(result, r.findRangeBounds(d));
3095: }
3096: }
3097: return result;
3098:
3099: }
3100:
3101: /**
3102: * Returns a list of the datasets that are mapped to the axis with the
3103: * specified index.
3104: *
3105: * @param axisIndex the axis index.
3106: *
3107: * @return The list (possibly empty, but never <code>null</code>).
3108: *
3109: * @since 1.0.3
3110: */
3111: private List datasetsMappedToDomainAxis(int axisIndex) {
3112: List result = new ArrayList();
3113: for (int datasetIndex = 0; datasetIndex < this .datasets.size(); datasetIndex++) {
3114: Object dataset = this .datasets.get(datasetIndex);
3115: if (dataset != null) {
3116: Integer m = (Integer) this .datasetToDomainAxisMap
3117: .get(datasetIndex);
3118: if (m == null) { // a dataset with no mapping is assigned to
3119: // axis 0
3120: if (axisIndex == 0) {
3121: result.add(dataset);
3122: }
3123: } else {
3124: if (m.intValue() == axisIndex) {
3125: result.add(dataset);
3126: }
3127: }
3128: }
3129: }
3130: return result;
3131: }
3132:
3133: /**
3134: * A utility method that returns a list of datasets that are mapped to a
3135: * given range axis.
3136: *
3137: * @param index the axis index.
3138: *
3139: * @return A list of datasets.
3140: */
3141: private List datasetsMappedToRangeAxis(int index) {
3142: List result = new ArrayList();
3143: for (int i = 0; i < this .datasets.size(); i++) {
3144: Object dataset = this .datasets.get(i);
3145: if (dataset != null) {
3146: Integer m = (Integer) this .datasetToRangeAxisMap.get(i);
3147: if (m == null) { // a dataset with no mapping is assigned to
3148: // axis 0
3149: if (index == 0) {
3150: result.add(dataset);
3151: }
3152: } else {
3153: if (m.intValue() == index) {
3154: result.add(dataset);
3155: }
3156: }
3157: }
3158: }
3159: return result;
3160: }
3161:
3162: /**
3163: * Returns the weight for this plot when it is used as a subplot within a
3164: * combined plot.
3165: *
3166: * @return The weight.
3167: *
3168: * @see #setWeight(int)
3169: */
3170: public int getWeight() {
3171: return this .weight;
3172: }
3173:
3174: /**
3175: * Sets the weight for the plot.
3176: *
3177: * @param weight the weight.
3178: *
3179: * @see #getWeight()
3180: */
3181: public void setWeight(int weight) {
3182: this .weight = weight;
3183: // TODO: notify?
3184: }
3185:
3186: /**
3187: * Returns the fixed domain axis space.
3188: *
3189: * @return The fixed domain axis space (possibly <code>null</code>).
3190: *
3191: * @see #setFixedDomainAxisSpace(AxisSpace)
3192: */
3193: public AxisSpace getFixedDomainAxisSpace() {
3194: return this .fixedDomainAxisSpace;
3195: }
3196:
3197: /**
3198: * Sets the fixed domain axis space.
3199: *
3200: * @param space the space (<code>null</code> permitted).
3201: *
3202: * @see #getFixedDomainAxisSpace()
3203: */
3204: public void setFixedDomainAxisSpace(AxisSpace space) {
3205: this .fixedDomainAxisSpace = space;
3206: // TODO: notify?
3207: }
3208:
3209: /**
3210: * Returns the fixed range axis space.
3211: *
3212: * @return The fixed range axis space (possibly <code>null</code>).
3213: *
3214: * @see #setFixedRangeAxisSpace(AxisSpace)
3215: */
3216: public AxisSpace getFixedRangeAxisSpace() {
3217: return this .fixedRangeAxisSpace;
3218: }
3219:
3220: /**
3221: * Sets the fixed range axis space.
3222: *
3223: * @param space the space (<code>null</code> permitted).
3224: *
3225: * @see #getFixedRangeAxisSpace()
3226: */
3227: public void setFixedRangeAxisSpace(AxisSpace space) {
3228: this .fixedRangeAxisSpace = space;
3229: // TODO: fire event?
3230: }
3231:
3232: /**
3233: * Returns a list of the categories in the plot's primary dataset.
3234: *
3235: * @return A list of the categories in the plot's primary dataset.
3236: *
3237: * @see #getCategoriesForAxis(CategoryAxis)
3238: */
3239: public List getCategories() {
3240: List result = null;
3241: if (getDataset() != null) {
3242: result = Collections.unmodifiableList(getDataset()
3243: .getColumnKeys());
3244: }
3245: return result;
3246: }
3247:
3248: /**
3249: * Returns a list of the categories that should be displayed for the
3250: * specified axis.
3251: *
3252: * @param axis the axis (<code>null</code> not permitted)
3253: *
3254: * @return The categories.
3255: *
3256: * @since 1.0.3
3257: */
3258: public List getCategoriesForAxis(CategoryAxis axis) {
3259: List result = new ArrayList();
3260: int axisIndex = this .domainAxes.indexOf(axis);
3261: List datasets = datasetsMappedToDomainAxis(axisIndex);
3262: Iterator iterator = datasets.iterator();
3263: while (iterator.hasNext()) {
3264: CategoryDataset dataset = (CategoryDataset) iterator.next();
3265: // add the unique categories from this dataset
3266: for (int i = 0; i < dataset.getColumnCount(); i++) {
3267: Comparable category = dataset.getColumnKey(i);
3268: if (!result.contains(category)) {
3269: result.add(category);
3270: }
3271: }
3272: }
3273: return result;
3274: }
3275:
3276: /**
3277: * Returns the flag that controls whether or not the shared domain axis is
3278: * drawn for each subplot.
3279: *
3280: * @return A boolean.
3281: *
3282: * @see #setDrawSharedDomainAxis(boolean)
3283: */
3284: public boolean getDrawSharedDomainAxis() {
3285: return this .drawSharedDomainAxis;
3286: }
3287:
3288: /**
3289: * Sets the flag that controls whether the shared domain axis is drawn when
3290: * this plot is being used as a subplot.
3291: *
3292: * @param draw a boolean.
3293: *
3294: * @see #getDrawSharedDomainAxis()
3295: */
3296: public void setDrawSharedDomainAxis(boolean draw) {
3297: this .drawSharedDomainAxis = draw;
3298: notifyListeners(new PlotChangeEvent(this ));
3299: }
3300:
3301: /**
3302: * Returns <code>false</code> to indicate that the domain axes are not
3303: * zoomable.
3304: *
3305: * @return A boolean.
3306: *
3307: * @see #isRangeZoomable()
3308: */
3309: public boolean isDomainZoomable() {
3310: return false;
3311: }
3312:
3313: /**
3314: * Returns <code>true</code> to indicate that the range axes are zoomable.
3315: *
3316: * @return A boolean.
3317: *
3318: * @see #isDomainZoomable()
3319: */
3320: public boolean isRangeZoomable() {
3321: return true;
3322: }
3323:
3324: /**
3325: * This method does nothing, because <code>CategoryPlot</code> doesn't
3326: * support zooming on the domain.
3327: *
3328: * @param factor the zoom factor.
3329: * @param state the plot state.
3330: * @param source the source point (in Java2D space) for the zoom.
3331: */
3332: public void zoomDomainAxes(double factor, PlotRenderingInfo state,
3333: Point2D source) {
3334: // can't zoom domain axis
3335: }
3336:
3337: /**
3338: * This method does nothing, because <code>CategoryPlot</code> doesn't
3339: * support zooming on the domain.
3340: *
3341: * @param lowerPercent the lower bound.
3342: * @param upperPercent the upper bound.
3343: * @param state the plot state.
3344: * @param source the source point (in Java2D space) for the zoom.
3345: */
3346: public void zoomDomainAxes(double lowerPercent,
3347: double upperPercent, PlotRenderingInfo state, Point2D source) {
3348: // can't zoom domain axis
3349: }
3350:
3351: /**
3352: * Multiplies the range on the range axis/axes by the specified factor.
3353: *
3354: * @param factor the zoom factor.
3355: * @param state the plot state.
3356: * @param source the source point (in Java2D space) for the zoom.
3357: */
3358: public void zoomRangeAxes(double factor, PlotRenderingInfo state,
3359: Point2D source) {
3360: for (int i = 0; i < this .rangeAxes.size(); i++) {
3361: ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
3362: if (rangeAxis != null) {
3363: rangeAxis.resizeRange(factor);
3364: }
3365: }
3366: }
3367:
3368: /**
3369: * Zooms in on the range axes.
3370: *
3371: * @param lowerPercent the lower bound.
3372: * @param upperPercent the upper bound.
3373: * @param state the plot state.
3374: * @param source the source point (in Java2D space) for the zoom.
3375: */
3376: public void zoomRangeAxes(double lowerPercent, double upperPercent,
3377: PlotRenderingInfo state, Point2D source) {
3378: for (int i = 0; i < this .rangeAxes.size(); i++) {
3379: ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
3380: if (rangeAxis != null) {
3381: rangeAxis.zoomRange(lowerPercent, upperPercent);
3382: }
3383: }
3384: }
3385:
3386: /**
3387: * Returns the anchor value.
3388: *
3389: * @return The anchor value.
3390: *
3391: * @see #setAnchorValue(double)
3392: */
3393: public double getAnchorValue() {
3394: return this .anchorValue;
3395: }
3396:
3397: /**
3398: * Sets the anchor value and sends a {@link PlotChangeEvent} to all
3399: * registered listeners.
3400: *
3401: * @param value the anchor value.
3402: *
3403: * @see #getAnchorValue()
3404: */
3405: public void setAnchorValue(double value) {
3406: setAnchorValue(value, true);
3407: }
3408:
3409: /**
3410: * Sets the anchor value and, if requested, sends a {@link PlotChangeEvent}
3411: * to all registered listeners.
3412: *
3413: * @param value the value.
3414: * @param notify notify listeners?
3415: *
3416: * @see #getAnchorValue()
3417: */
3418: public void setAnchorValue(double value, boolean notify) {
3419: this .anchorValue = value;
3420: if (notify) {
3421: notifyListeners(new PlotChangeEvent(this ));
3422: }
3423: }
3424:
3425: /**
3426: * Tests the plot for equality with an arbitrary object.
3427: *
3428: * @param obj the object to test against (<code>null</code> permitted).
3429: *
3430: * @return A boolean.
3431: */
3432: public boolean equals(Object obj) {
3433:
3434: if (obj == this ) {
3435: return true;
3436: }
3437: if (!(obj instanceof CategoryPlot)) {
3438: return false;
3439: }
3440: if (!super .equals(obj)) {
3441: return false;
3442: }
3443:
3444: CategoryPlot that = (CategoryPlot) obj;
3445:
3446: if (this .orientation != that.orientation) {
3447: return false;
3448: }
3449: if (!ObjectUtilities.equal(this .axisOffset, that.axisOffset)) {
3450: return false;
3451: }
3452: if (!this .domainAxes.equals(that.domainAxes)) {
3453: return false;
3454: }
3455: if (!this .domainAxisLocations.equals(that.domainAxisLocations)) {
3456: return false;
3457: }
3458: if (this .drawSharedDomainAxis != that.drawSharedDomainAxis) {
3459: return false;
3460: }
3461: if (!this .rangeAxes.equals(that.rangeAxes)) {
3462: return false;
3463: }
3464: if (!this .rangeAxisLocations.equals(that.rangeAxisLocations)) {
3465: return false;
3466: }
3467: if (!ObjectUtilities.equal(this .datasetToDomainAxisMap,
3468: that.datasetToDomainAxisMap)) {
3469: return false;
3470: }
3471: if (!ObjectUtilities.equal(this .datasetToRangeAxisMap,
3472: that.datasetToRangeAxisMap)) {
3473: return false;
3474: }
3475: if (!ObjectUtilities.equal(this .renderers, that.renderers)) {
3476: return false;
3477: }
3478: if (this .renderingOrder != that.renderingOrder) {
3479: return false;
3480: }
3481: if (this .columnRenderingOrder != that.columnRenderingOrder) {
3482: return false;
3483: }
3484: if (this .rowRenderingOrder != that.rowRenderingOrder) {
3485: return false;
3486: }
3487: if (this .domainGridlinesVisible != that.domainGridlinesVisible) {
3488: return false;
3489: }
3490: if (this .domainGridlinePosition != that.domainGridlinePosition) {
3491: return false;
3492: }
3493: if (!ObjectUtilities.equal(this .domainGridlineStroke,
3494: that.domainGridlineStroke)) {
3495: return false;
3496: }
3497: if (!PaintUtilities.equal(this .domainGridlinePaint,
3498: that.domainGridlinePaint)) {
3499: return false;
3500: }
3501: if (this .rangeGridlinesVisible != that.rangeGridlinesVisible) {
3502: return false;
3503: }
3504: if (!ObjectUtilities.equal(this .rangeGridlineStroke,
3505: that.rangeGridlineStroke)) {
3506: return false;
3507: }
3508: if (!PaintUtilities.equal(this .rangeGridlinePaint,
3509: that.rangeGridlinePaint)) {
3510: return false;
3511: }
3512: if (this .anchorValue != that.anchorValue) {
3513: return false;
3514: }
3515: if (this .rangeCrosshairVisible != that.rangeCrosshairVisible) {
3516: return false;
3517: }
3518: if (this .rangeCrosshairValue != that.rangeCrosshairValue) {
3519: return false;
3520: }
3521: if (!ObjectUtilities.equal(this .rangeCrosshairStroke,
3522: that.rangeCrosshairStroke)) {
3523: return false;
3524: }
3525: if (!PaintUtilities.equal(this .rangeCrosshairPaint,
3526: that.rangeCrosshairPaint)) {
3527: return false;
3528: }
3529: if (this .rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData) {
3530: return false;
3531: }
3532: if (!ObjectUtilities.equal(this .foregroundRangeMarkers,
3533: that.foregroundRangeMarkers)) {
3534: return false;
3535: }
3536: if (!ObjectUtilities.equal(this .backgroundRangeMarkers,
3537: that.backgroundRangeMarkers)) {
3538: return false;
3539: }
3540: if (!ObjectUtilities.equal(this .annotations, that.annotations)) {
3541: return false;
3542: }
3543: if (this .weight != that.weight) {
3544: return false;
3545: }
3546: if (!ObjectUtilities.equal(this .fixedDomainAxisSpace,
3547: that.fixedDomainAxisSpace)) {
3548: return false;
3549: }
3550: if (!ObjectUtilities.equal(this .fixedRangeAxisSpace,
3551: that.fixedRangeAxisSpace)) {
3552: return false;
3553: }
3554:
3555: return true;
3556:
3557: }
3558:
3559: /**
3560: * Returns a clone of the plot.
3561: *
3562: * @return A clone.
3563: *
3564: * @throws CloneNotSupportedException if the cloning is not supported.
3565: */
3566: public Object clone() throws CloneNotSupportedException {
3567:
3568: CategoryPlot clone = (CategoryPlot) super .clone();
3569:
3570: clone.domainAxes = new ObjectList();
3571: for (int i = 0; i < this .domainAxes.size(); i++) {
3572: CategoryAxis xAxis = (CategoryAxis) this .domainAxes.get(i);
3573: if (xAxis != null) {
3574: CategoryAxis clonedAxis = (CategoryAxis) xAxis.clone();
3575: clone.setDomainAxis(i, clonedAxis);
3576: }
3577: }
3578: clone.domainAxisLocations = (ObjectList) this .domainAxisLocations
3579: .clone();
3580:
3581: clone.rangeAxes = new ObjectList();
3582: for (int i = 0; i < this .rangeAxes.size(); i++) {
3583: ValueAxis yAxis = (ValueAxis) this .rangeAxes.get(i);
3584: if (yAxis != null) {
3585: ValueAxis clonedAxis = (ValueAxis) yAxis.clone();
3586: clone.setRangeAxis(i, clonedAxis);
3587: }
3588: }
3589: clone.rangeAxisLocations = (ObjectList) this .rangeAxisLocations
3590: .clone();
3591:
3592: clone.datasets = (ObjectList) this .datasets.clone();
3593: for (int i = 0; i < clone.datasets.size(); i++) {
3594: CategoryDataset dataset = clone.getDataset(i);
3595: if (dataset != null) {
3596: dataset.addChangeListener(clone);
3597: }
3598: }
3599: clone.datasetToDomainAxisMap = (ObjectList) this .datasetToDomainAxisMap
3600: .clone();
3601: clone.datasetToRangeAxisMap = (ObjectList) this .datasetToRangeAxisMap
3602: .clone();
3603: clone.renderers = (ObjectList) this .renderers.clone();
3604: if (this .fixedDomainAxisSpace != null) {
3605: clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities
3606: .clone(this .fixedDomainAxisSpace);
3607: }
3608: if (this .fixedRangeAxisSpace != null) {
3609: clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities
3610: .clone(this .fixedRangeAxisSpace);
3611: }
3612:
3613: return clone;
3614:
3615: }
3616:
3617: /**
3618: * Provides serialization support.
3619: *
3620: * @param stream the output stream.
3621: *
3622: * @throws IOException if there is an I/O error.
3623: */
3624: private void writeObject(ObjectOutputStream stream)
3625: throws IOException {
3626: stream.defaultWriteObject();
3627: SerialUtilities.writeStroke(this .domainGridlineStroke, stream);
3628: SerialUtilities.writePaint(this .domainGridlinePaint, stream);
3629: SerialUtilities.writeStroke(this .rangeGridlineStroke, stream);
3630: SerialUtilities.writePaint(this .rangeGridlinePaint, stream);
3631: SerialUtilities.writeStroke(this .rangeCrosshairStroke, stream);
3632: SerialUtilities.writePaint(this .rangeCrosshairPaint, stream);
3633: }
3634:
3635: /**
3636: * Provides serialization support.
3637: *
3638: * @param stream the input stream.
3639: *
3640: * @throws IOException if there is an I/O error.
3641: * @throws ClassNotFoundException if there is a classpath problem.
3642: */
3643: private void readObject(ObjectInputStream stream)
3644: throws IOException, ClassNotFoundException {
3645:
3646: stream.defaultReadObject();
3647: this .domainGridlineStroke = SerialUtilities.readStroke(stream);
3648: this .domainGridlinePaint = SerialUtilities.readPaint(stream);
3649: this .rangeGridlineStroke = SerialUtilities.readStroke(stream);
3650: this .rangeGridlinePaint = SerialUtilities.readPaint(stream);
3651: this .rangeCrosshairStroke = SerialUtilities.readStroke(stream);
3652: this .rangeCrosshairPaint = SerialUtilities.readPaint(stream);
3653:
3654: for (int i = 0; i < this .domainAxes.size(); i++) {
3655: CategoryAxis xAxis = (CategoryAxis) this .domainAxes.get(i);
3656: if (xAxis != null) {
3657: xAxis.setPlot(this );
3658: xAxis.addChangeListener(this );
3659: }
3660: }
3661: for (int i = 0; i < this .rangeAxes.size(); i++) {
3662: ValueAxis yAxis = (ValueAxis) this .rangeAxes.get(i);
3663: if (yAxis != null) {
3664: yAxis.setPlot(this );
3665: yAxis.addChangeListener(this );
3666: }
3667: }
3668: int datasetCount = this .datasets.size();
3669: for (int i = 0; i < datasetCount; i++) {
3670: Dataset dataset = (Dataset) this .datasets.get(i);
3671: if (dataset != null) {
3672: dataset.addChangeListener(this );
3673: }
3674: }
3675: int rendererCount = this .renderers.size();
3676: for (int i = 0; i < rendererCount; i++) {
3677: CategoryItemRenderer renderer = (CategoryItemRenderer) this.renderers
3678: .get(i);
3679: if (renderer != null) {
3680: renderer.addChangeListener(this);
3681: }
3682: }
3683:
3684: }
3685:
3686: }
|