Source Code Cross Referenced for XYPlot.java in  » Chart » jfreechart » org » jfree » chart » plot » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Chart » jfreechart » org.jfree.chart.plot 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:         * XYPlot.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):   Craig MacFarlane;
0034:         *                   Mark Watson (www.markwatson.com);
0035:         *                   Jonathan Nash;
0036:         *                   Gideon Krause;
0037:         *                   Klaus Rheinwald;
0038:         *                   Xavier Poinsard;
0039:         *                   Richard Atkinson;
0040:         *                   Arnaud Lelievre;
0041:         *                   Nicolas Brodu;
0042:         *                   Eduardo Ramalho;
0043:         *                   Sergei Ivanov;
0044:         *
0045:         * $Id: XYPlot.java,v 1.44.2.29 2007/06/07 12:49:36 mungady Exp $
0046:         *
0047:         * Changes (from 21-Jun-2001)
0048:         * --------------------------
0049:         * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
0050:         * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
0051:         * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
0052:         * 19-Oct-2001 : Removed the code for drawing the visual representation of each
0053:         *               data point into a separate class StandardXYItemRenderer.
0054:         *               This will make it easier to add variations to the way the
0055:         *               charts are drawn.  Based on code contributed by Mark
0056:         *               Watson (DG);
0057:         * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
0058:         * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
0059:         *               inside JScrollPane (DG);
0060:         * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
0061:         * 13-Dec-2001 : Added skeleton code for tooltips.  Added new constructor. (DG);
0062:         * 16-Jan-2002 : Renamed the tooltips class (DG);
0063:         * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
0064:         *               Crosshairs based on code by Jonathan Nash (DG);
0065:         * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
0066:         *               Vieujot (DG);
0067:         * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
0068:         *               special case when chart is null (DG);
0069:         * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
0070:         * 28-Mar-2002 : The plot now registers with the renderer as a property change
0071:         *               listener.  Also added a new constructor (DG);
0072:         * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
0073:         *               method.  Moved the tooltip generator into the renderer (DG);
0074:         * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
0075:         *               lines (DG);
0076:         * 13-May-2002 : Small change to the draw() method so that it works for
0077:         *               OverlaidXYPlot also (DG);
0078:         * 25-Jun-2002 : Removed redundant import (DG);
0079:         * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
0080:         *               setXYItemRenderer() --> setRenderer() (DG);
0081:         * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
0082:         * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
0083:         * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
0084:         *               these were set in the axes) (DG);
0085:         * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
0086:         *               border bug fix contributed by Gideon Krause (DG);
0087:         * 22-Jan-2003 : Removed monolithic constructor (DG);
0088:         * 04-Mar-2003 : Added 'no data' message, see bug report 691634.  Added
0089:         *               secondary range markers using code contributed by Klaus
0090:         *               Rheinwald (DG);
0091:         * 26-Mar-2003 : Implemented Serializable (DG);
0092:         * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
0093:         * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
0094:         * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
0095:         * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
0096:         * 15-May-2003 : Added an orientation attribute (DG);
0097:         * 02-Jun-2003 : Removed range axis compatibility test (DG);
0098:         * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
0099:         *               Services Ltd) (DG);
0100:         * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
0101:         * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
0102:         *               overlaid plots) (DG);
0103:         * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
0104:         *               renderers (DG);
0105:         * 27-Jul-2003 : Added support for stacked XY area charts (RA);
0106:         * 19-Aug-2003 : Implemented Cloneable (DG);
0107:         * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
0108:         *               change event (797466) (DG)
0109:         * 08-Sep-2003 : Added internationalization via use of properties
0110:         *               resourceBundle (RFE 690236) (AL);
0111:         * 08-Sep-2003 : Changed ValueAxis API (DG);
0112:         * 08-Sep-2003 : Fixes for serialization (NB);
0113:         * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0114:         * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
0115:         * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
0116:         *               getSecondaryRangeAxisCount() methods suggested by Eduardo
0117:         *               Ramalho (RFE 808548) (DG);
0118:         * 23-Sep-2003 : Split domain and range markers into foreground and
0119:         *               background (DG);
0120:         * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
0121:         *               methods.  Fixed bug (815876) in addSecondaryRangeMarker()
0122:         *               method.  Added new addSecondaryDomainMarker methods (see bug
0123:         *               id 815869) (DG);
0124:         * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
0125:         *               requested by Eduardo Ramalho (DG);
0126:         * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
0127:         *               values (DG);
0128:         * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
0129:         * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
0130:         * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
0131:         *               range type (DG);
0132:         * 22-Mar-2004 : Fixed cloning bug (DG);
0133:         * 23-Mar-2004 : Fixed more cloning bugs (DG);
0134:         * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
0135:         *               stacked, see this post in the forum:
0136:         *               http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
0137:         * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
0138:         * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
0139:         *               plot (DG);
0140:         * 27-Apr-2004 : Removed major distinction between primary and secondary
0141:         *               datasets, renderers and axes (DG);
0142:         * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
0143:         *               renderer interface (DG);
0144:         * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
0145:         * 19-May-2004 : Added indexOf() method (DG);
0146:         * 03-Jun-2004 : Fixed zooming bug (DG);
0147:         * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
0148:         * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
0149:         * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
0150:         *               the x-value range (now matches behaviour for y-values).  Added
0151:         *               getDomainAxisIndex() method (DG);
0152:         * 12-Nov-2004 : Implemented new Zoomable interface (DG);
0153:         * 25-Nov-2004 : Small update to clone() implementation (DG);
0154:         * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
0155:         * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
0156:         * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
0157:         * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
0158:         * 26-Apr-2005 : Removed LOGGER (DG);
0159:         * 04-May-2005 : Fixed serialization of domain and range markers (DG);
0160:         * 05-May-2005 : Removed unused draw() method (DG);
0161:         * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
0162:         *               RFE 1183100 (DG);
0163:         * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
0164:         *               axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
0165:         * 01-Jun-2005 : Added clearDomainMarkers(int) method to match 
0166:         *               clearRangeMarkers(int) (DG);
0167:         * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
0168:         * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
0169:         * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG);
0170:         * ------------- JFREECHART 1.0.x ---------------------------------------------
0171:         * 26-Jan-2006 : Added getAnnotations() method (DG);
0172:         * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
0173:         * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report 
0174:         *               1565168 (DG);
0175:         * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus 
0176:         *               API doc updates (DG);
0177:         * 29-Nov-2006 : Added argument checks (DG);
0178:         * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG);
0179:         * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG);
0180:         * 26-Feb-2007 : Added missing setDomainAxisLocation() and 
0181:         *               setRangeAxisLocation() methods (DG);
0182:         * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation
0183:         *               (see patch 1671648 by Sergei Ivanov) (DG);
0184:         * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG);
0185:         * 23-Mar-2007 : Added domain zero base line facility (DG);
0186:         * 04-May-2007 : Render only visible data items if possible (DG);
0187:         * 24-May-2007 : Fixed bug in render method for an empty series (DG);
0188:         * 07-Jun-2007 : Modified drawBackground() to pass orientation to 
0189:         *               fillBackground() for handling GradientPaint (DG);
0190:         *
0191:         */
0192:
0193:        package org.jfree.chart.plot;
0194:
0195:        import java.awt.AlphaComposite;
0196:        import java.awt.BasicStroke;
0197:        import java.awt.Color;
0198:        import java.awt.Composite;
0199:        import java.awt.Graphics2D;
0200:        import java.awt.Paint;
0201:        import java.awt.Shape;
0202:        import java.awt.Stroke;
0203:        import java.awt.geom.Line2D;
0204:        import java.awt.geom.Point2D;
0205:        import java.awt.geom.Rectangle2D;
0206:        import java.io.IOException;
0207:        import java.io.ObjectInputStream;
0208:        import java.io.ObjectOutputStream;
0209:        import java.io.Serializable;
0210:        import java.util.ArrayList;
0211:        import java.util.Collection;
0212:        import java.util.Collections;
0213:        import java.util.HashMap;
0214:        import java.util.Iterator;
0215:        import java.util.List;
0216:        import java.util.Map;
0217:        import java.util.ResourceBundle;
0218:        import java.util.Set;
0219:        import java.util.TreeMap;
0220:
0221:        import org.jfree.chart.LegendItem;
0222:        import org.jfree.chart.LegendItemCollection;
0223:        import org.jfree.chart.annotations.XYAnnotation;
0224:        import org.jfree.chart.axis.Axis;
0225:        import org.jfree.chart.axis.AxisCollection;
0226:        import org.jfree.chart.axis.AxisLocation;
0227:        import org.jfree.chart.axis.AxisSpace;
0228:        import org.jfree.chart.axis.AxisState;
0229:        import org.jfree.chart.axis.ValueAxis;
0230:        import org.jfree.chart.axis.ValueTick;
0231:        import org.jfree.chart.event.ChartChangeEventType;
0232:        import org.jfree.chart.event.PlotChangeEvent;
0233:        import org.jfree.chart.event.RendererChangeEvent;
0234:        import org.jfree.chart.event.RendererChangeListener;
0235:        import org.jfree.chart.renderer.RendererUtilities;
0236:        import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
0237:        import org.jfree.chart.renderer.xy.XYItemRenderer;
0238:        import org.jfree.chart.renderer.xy.XYItemRendererState;
0239:        import org.jfree.data.Range;
0240:        import org.jfree.data.general.Dataset;
0241:        import org.jfree.data.general.DatasetChangeEvent;
0242:        import org.jfree.data.general.DatasetUtilities;
0243:        import org.jfree.data.xy.XYDataset;
0244:        import org.jfree.io.SerialUtilities;
0245:        import org.jfree.ui.Layer;
0246:        import org.jfree.ui.RectangleEdge;
0247:        import org.jfree.ui.RectangleInsets;
0248:        import org.jfree.util.ObjectList;
0249:        import org.jfree.util.ObjectUtilities;
0250:        import org.jfree.util.PaintUtilities;
0251:        import org.jfree.util.PublicCloneable;
0252:
0253:        /**
0254:         * A general class for plotting data in the form of (x, y) pairs.  This plot can
0255:         * use data from any class that implements the {@link XYDataset} interface.
0256:         * <P>
0257:         * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
0258:         * on the plot.  By using different renderers, various chart types can be
0259:         * produced.
0260:         * <p>
0261:         * The {@link org.jfree.chart.ChartFactory} class contains static methods for
0262:         * creating pre-configured charts.
0263:         */
0264:        public class XYPlot extends Plot implements  ValueAxisPlot, Zoomable,
0265:                RendererChangeListener, Cloneable, PublicCloneable,
0266:                Serializable {
0267:
0268:            /** For serialization. */
0269:            private static final long serialVersionUID = 7044148245716569264L;
0270:
0271:            /** The default grid line stroke. */
0272:            public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(
0273:                    0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
0274:                    new float[] { 2.0f, 2.0f }, 0.0f);
0275:
0276:            /** The default grid line paint. */
0277:            public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
0278:
0279:            /** The default crosshair visibility. */
0280:            public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
0281:
0282:            /** The default crosshair stroke. */
0283:            public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE;
0284:
0285:            /** The default crosshair paint. */
0286:            public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
0287:
0288:            /** The resourceBundle for the localization. */
0289:            protected static ResourceBundle localizationResources = ResourceBundle
0290:                    .getBundle("org.jfree.chart.plot.LocalizationBundle");
0291:
0292:            /** The plot orientation. */
0293:            private PlotOrientation orientation;
0294:
0295:            /** The offset between the data area and the axes. */
0296:            private RectangleInsets axisOffset;
0297:
0298:            /** The domain axis / axes (used for the x-values). */
0299:            private ObjectList domainAxes;
0300:
0301:            /** The domain axis locations. */
0302:            private ObjectList domainAxisLocations;
0303:
0304:            /** The range axis (used for the y-values). */
0305:            private ObjectList rangeAxes;
0306:
0307:            /** The range axis location. */
0308:            private ObjectList rangeAxisLocations;
0309:
0310:            /** Storage for the datasets. */
0311:            private ObjectList datasets;
0312:
0313:            /** Storage for the renderers. */
0314:            private ObjectList renderers;
0315:
0316:            /**
0317:             * Storage for keys that map datasets/renderers to domain axes.  If the
0318:             * map contains no entry for a dataset, it is assumed to map to the
0319:             * primary domain axis (index = 0).
0320:             */
0321:            private Map datasetToDomainAxisMap;
0322:
0323:            /**
0324:             * Storage for keys that map datasets/renderers to range axes. If the
0325:             * map contains no entry for a dataset, it is assumed to map to the
0326:             * primary domain axis (index = 0).
0327:             */
0328:            private Map datasetToRangeAxisMap;
0329:
0330:            /** The origin point for the quadrants (if drawn). */
0331:            private transient Point2D quadrantOrigin = new Point2D.Double(0.0,
0332:                    0.0);
0333:
0334:            /** The paint used for each quadrant. */
0335:            private transient Paint[] quadrantPaint = new Paint[] { null, null,
0336:                    null, null };
0337:
0338:            /** A flag that controls whether the domain grid-lines are visible. */
0339:            private boolean domainGridlinesVisible;
0340:
0341:            /** The stroke used to draw the domain grid-lines. */
0342:            private transient Stroke domainGridlineStroke;
0343:
0344:            /** The paint used to draw the domain grid-lines. */
0345:            private transient Paint domainGridlinePaint;
0346:
0347:            /** A flag that controls whether the range grid-lines are visible. */
0348:            private boolean rangeGridlinesVisible;
0349:
0350:            /** The stroke used to draw the range grid-lines. */
0351:            private transient Stroke rangeGridlineStroke;
0352:
0353:            /** The paint used to draw the range grid-lines. */
0354:            private transient Paint rangeGridlinePaint;
0355:
0356:            /** 
0357:             * A flag that controls whether or not the zero baseline against the domain
0358:             * axis is visible.
0359:             * 
0360:             * @since 1.0.5
0361:             */
0362:            private boolean domainZeroBaselineVisible;
0363:
0364:            /** 
0365:             * The stroke used for the zero baseline against the domain axis. 
0366:             * 
0367:             * @since 1.0.5
0368:             */
0369:            private transient Stroke domainZeroBaselineStroke;
0370:
0371:            /** 
0372:             * The paint used for the zero baseline against the domain axis. 
0373:             * 
0374:             * @since 1.0.5
0375:             */
0376:            private transient Paint domainZeroBaselinePaint;
0377:
0378:            /** 
0379:             * A flag that controls whether or not the zero baseline against the range
0380:             * axis is visible.
0381:             */
0382:            private boolean rangeZeroBaselineVisible;
0383:
0384:            /** The stroke used for the zero baseline against the range axis. */
0385:            private transient Stroke rangeZeroBaselineStroke;
0386:
0387:            /** The paint used for the zero baseline against the range axis. */
0388:            private transient Paint rangeZeroBaselinePaint;
0389:
0390:            /** A flag that controls whether or not a domain crosshair is drawn..*/
0391:            private boolean domainCrosshairVisible;
0392:
0393:            /** The domain crosshair value. */
0394:            private double domainCrosshairValue;
0395:
0396:            /** The pen/brush used to draw the crosshair (if any). */
0397:            private transient Stroke domainCrosshairStroke;
0398:
0399:            /** The color used to draw the crosshair (if any). */
0400:            private transient Paint domainCrosshairPaint;
0401:
0402:            /**
0403:             * A flag that controls whether or not the crosshair locks onto actual
0404:             * data points.
0405:             */
0406:            private boolean domainCrosshairLockedOnData = true;
0407:
0408:            /** A flag that controls whether or not a range crosshair is drawn..*/
0409:            private boolean rangeCrosshairVisible;
0410:
0411:            /** The range crosshair value. */
0412:            private double rangeCrosshairValue;
0413:
0414:            /** The pen/brush used to draw the crosshair (if any). */
0415:            private transient Stroke rangeCrosshairStroke;
0416:
0417:            /** The color used to draw the crosshair (if any). */
0418:            private transient Paint rangeCrosshairPaint;
0419:
0420:            /**
0421:             * A flag that controls whether or not the crosshair locks onto actual
0422:             * data points.
0423:             */
0424:            private boolean rangeCrosshairLockedOnData = true;
0425:
0426:            /** A map of lists of foreground markers (optional) for the domain axes. */
0427:            private Map foregroundDomainMarkers;
0428:
0429:            /** A map of lists of background markers (optional) for the domain axes. */
0430:            private Map backgroundDomainMarkers;
0431:
0432:            /** A map of lists of foreground markers (optional) for the range axes. */
0433:            private Map foregroundRangeMarkers;
0434:
0435:            /** A map of lists of background markers (optional) for the range axes. */
0436:            private Map backgroundRangeMarkers;
0437:
0438:            /** 
0439:             * A (possibly empty) list of annotations for the plot.  The list should
0440:             * be initialised in the constructor and never allowed to be 
0441:             * <code>null</code>.
0442:             */
0443:            private List annotations;
0444:
0445:            /** The paint used for the domain tick bands (if any). */
0446:            private transient Paint domainTickBandPaint;
0447:
0448:            /** The paint used for the range tick bands (if any). */
0449:            private transient Paint rangeTickBandPaint;
0450:
0451:            /** The fixed domain axis space. */
0452:            private AxisSpace fixedDomainAxisSpace;
0453:
0454:            /** The fixed range axis space. */
0455:            private AxisSpace fixedRangeAxisSpace;
0456:
0457:            /**
0458:             * The order of the dataset rendering (REVERSE draws the primary dataset
0459:             * last so that it appears to be on top).
0460:             */
0461:            private DatasetRenderingOrder datasetRenderingOrder = DatasetRenderingOrder.REVERSE;
0462:
0463:            /**
0464:             * The order of the series rendering (REVERSE draws the primary series
0465:             * last so that it appears to be on top).
0466:             */
0467:            private SeriesRenderingOrder seriesRenderingOrder = SeriesRenderingOrder.REVERSE;
0468:
0469:            /**
0470:             * The weight for this plot (only relevant if this is a subplot in a
0471:             * combined plot).
0472:             */
0473:            private int weight;
0474:
0475:            /**
0476:             * An optional collection of legend items that can be returned by the
0477:             * getLegendItems() method.
0478:             */
0479:            private LegendItemCollection fixedLegendItems;
0480:
0481:            /**
0482:             * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
0483:             * no renderer.  You should specify these items before using the plot.
0484:             */
0485:            public XYPlot() {
0486:                this (null, null, null, null);
0487:            }
0488:
0489:            /**
0490:             * Creates a new plot with the specified dataset, axes and renderer.  Any
0491:             * of the arguments can be <code>null</code>, but in that case you should
0492:             * take care to specify the value before using the plot (otherwise a
0493:             * <code>NullPointerException</code> may be thrown).
0494:             *
0495:             * @param dataset  the dataset (<code>null</code> permitted).
0496:             * @param domainAxis  the domain axis (<code>null</code> permitted).
0497:             * @param rangeAxis  the range axis (<code>null</code> permitted).
0498:             * @param renderer  the renderer (<code>null</code> permitted).
0499:             */
0500:            public XYPlot(XYDataset dataset, ValueAxis domainAxis,
0501:                    ValueAxis rangeAxis, XYItemRenderer renderer) {
0502:
0503:                super ();
0504:
0505:                this .orientation = PlotOrientation.VERTICAL;
0506:                this .weight = 1; // only relevant when this is a subplot
0507:                this .axisOffset = RectangleInsets.ZERO_INSETS;
0508:
0509:                // allocate storage for datasets, axes and renderers (all optional)
0510:                this .domainAxes = new ObjectList();
0511:                this .domainAxisLocations = new ObjectList();
0512:                this .foregroundDomainMarkers = new HashMap();
0513:                this .backgroundDomainMarkers = new HashMap();
0514:
0515:                this .rangeAxes = new ObjectList();
0516:                this .rangeAxisLocations = new ObjectList();
0517:                this .foregroundRangeMarkers = new HashMap();
0518:                this .backgroundRangeMarkers = new HashMap();
0519:
0520:                this .datasets = new ObjectList();
0521:                this .renderers = new ObjectList();
0522:
0523:                this .datasetToDomainAxisMap = new TreeMap();
0524:                this .datasetToRangeAxisMap = new TreeMap();
0525:
0526:                this .datasets.set(0, dataset);
0527:                if (dataset != null) {
0528:                    dataset.addChangeListener(this );
0529:                }
0530:
0531:                this .renderers.set(0, renderer);
0532:                if (renderer != null) {
0533:                    renderer.setPlot(this );
0534:                    renderer.addChangeListener(this );
0535:                }
0536:
0537:                this .domainAxes.set(0, domainAxis);
0538:                this .mapDatasetToDomainAxis(0, 0);
0539:                if (domainAxis != null) {
0540:                    domainAxis.setPlot(this );
0541:                    domainAxis.addChangeListener(this );
0542:                }
0543:                this .domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
0544:
0545:                this .rangeAxes.set(0, rangeAxis);
0546:                this .mapDatasetToRangeAxis(0, 0);
0547:                if (rangeAxis != null) {
0548:                    rangeAxis.setPlot(this );
0549:                    rangeAxis.addChangeListener(this );
0550:                }
0551:                this .rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
0552:
0553:                configureDomainAxes();
0554:                configureRangeAxes();
0555:
0556:                this .domainGridlinesVisible = true;
0557:                this .domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0558:                this .domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0559:
0560:                this .domainZeroBaselineVisible = false;
0561:                this .domainZeroBaselinePaint = Color.black;
0562:                this .domainZeroBaselineStroke = new BasicStroke(0.5f);
0563:
0564:                this .rangeGridlinesVisible = true;
0565:                this .rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0566:                this .rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0567:
0568:                this .rangeZeroBaselineVisible = false;
0569:                this .rangeZeroBaselinePaint = Color.black;
0570:                this .rangeZeroBaselineStroke = new BasicStroke(0.5f);
0571:
0572:                this .domainCrosshairVisible = false;
0573:                this .domainCrosshairValue = 0.0;
0574:                this .domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
0575:                this .domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
0576:
0577:                this .rangeCrosshairVisible = false;
0578:                this .rangeCrosshairValue = 0.0;
0579:                this .rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
0580:                this .rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
0581:
0582:                this .annotations = new java.util.ArrayList();
0583:
0584:            }
0585:
0586:            /**
0587:             * Returns the plot type as a string.
0588:             *
0589:             * @return A short string describing the type of plot.
0590:             */
0591:            public String getPlotType() {
0592:                return localizationResources.getString("XY_Plot");
0593:            }
0594:
0595:            /**
0596:             * Returns the orientation of the plot.
0597:             *
0598:             * @return The orientation (never <code>null</code>).
0599:             * 
0600:             * @see #setOrientation(PlotOrientation)
0601:             */
0602:            public PlotOrientation getOrientation() {
0603:                return this .orientation;
0604:            }
0605:
0606:            /**
0607:             * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
0608:             * all registered listeners.
0609:             *
0610:             * @param orientation  the orientation (<code>null</code> not allowed).
0611:             * 
0612:             * @see #getOrientation()
0613:             */
0614:            public void setOrientation(PlotOrientation orientation) {
0615:                if (orientation == null) {
0616:                    throw new IllegalArgumentException(
0617:                            "Null 'orientation' argument.");
0618:                }
0619:                if (orientation != this .orientation) {
0620:                    this .orientation = orientation;
0621:                    notifyListeners(new PlotChangeEvent(this ));
0622:                }
0623:            }
0624:
0625:            /**
0626:             * Returns the axis offset.
0627:             *
0628:             * @return The axis offset (never <code>null</code>).
0629:             * 
0630:             * @see #setAxisOffset(RectangleInsets)
0631:             */
0632:            public RectangleInsets getAxisOffset() {
0633:                return this .axisOffset;
0634:            }
0635:
0636:            /**
0637:             * Sets the axis offsets (gap between the data area and the axes) and sends
0638:             * a {@link PlotChangeEvent} to all registered listeners.
0639:             *
0640:             * @param offset  the offset (<code>null</code> not permitted).
0641:             * 
0642:             * @see #getAxisOffset()
0643:             */
0644:            public void setAxisOffset(RectangleInsets offset) {
0645:                if (offset == null) {
0646:                    throw new IllegalArgumentException(
0647:                            "Null 'offset' argument.");
0648:                }
0649:                this .axisOffset = offset;
0650:                notifyListeners(new PlotChangeEvent(this ));
0651:            }
0652:
0653:            /**
0654:             * Returns the domain axis with index 0.  If the domain axis for this plot
0655:             * is <code>null</code>, then the method will return the parent plot's 
0656:             * domain axis (if there is a parent plot).
0657:             *
0658:             * @return The domain axis (possibly <code>null</code>).
0659:             * 
0660:             * @see #getDomainAxis(int)
0661:             * @see #setDomainAxis(ValueAxis)
0662:             */
0663:            public ValueAxis getDomainAxis() {
0664:                return getDomainAxis(0);
0665:            }
0666:
0667:            /**
0668:             * Returns the domain axis with the specified index, or <code>null</code>.
0669:             *
0670:             * @param index  the axis index.
0671:             *
0672:             * @return The axis (<code>null</code> possible).
0673:             * 
0674:             * @see #setDomainAxis(int, ValueAxis)
0675:             */
0676:            public ValueAxis getDomainAxis(int index) {
0677:                ValueAxis result = null;
0678:                if (index < this .domainAxes.size()) {
0679:                    result = (ValueAxis) this .domainAxes.get(index);
0680:                }
0681:                if (result == null) {
0682:                    Plot parent = getParent();
0683:                    if (parent instanceof  XYPlot) {
0684:                        XYPlot xy = (XYPlot) parent;
0685:                        result = xy.getDomainAxis(index);
0686:                    }
0687:                }
0688:                return result;
0689:            }
0690:
0691:            /**
0692:             * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
0693:             * to all registered listeners.
0694:             *
0695:             * @param axis  the new axis (<code>null</code> permitted).
0696:             * 
0697:             * @see #getDomainAxis()
0698:             * @see #setDomainAxis(int, ValueAxis)
0699:             */
0700:            public void setDomainAxis(ValueAxis axis) {
0701:                setDomainAxis(0, axis);
0702:            }
0703:
0704:            /**
0705:             * Sets a domain axis and sends a {@link PlotChangeEvent} to all
0706:             * registered listeners.
0707:             *
0708:             * @param index  the axis index.
0709:             * @param axis  the axis (<code>null</code> permitted).
0710:             * 
0711:             * @see #getDomainAxis(int)
0712:             * @see #setRangeAxis(int, ValueAxis)
0713:             */
0714:            public void setDomainAxis(int index, ValueAxis axis) {
0715:                setDomainAxis(index, axis, true);
0716:            }
0717:
0718:            /**
0719:             * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
0720:             * all registered listeners.
0721:             *
0722:             * @param index  the axis index.
0723:             * @param axis  the axis.
0724:             * @param notify  notify listeners?
0725:             * 
0726:             * @see #getDomainAxis(int)
0727:             */
0728:            public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
0729:                ValueAxis existing = getDomainAxis(index);
0730:                if (existing != null) {
0731:                    existing.removeChangeListener(this );
0732:                }
0733:                if (axis != null) {
0734:                    axis.setPlot(this );
0735:                }
0736:                this .domainAxes.set(index, axis);
0737:                if (axis != null) {
0738:                    axis.configure();
0739:                    axis.addChangeListener(this );
0740:                }
0741:                if (notify) {
0742:                    notifyListeners(new PlotChangeEvent(this ));
0743:                }
0744:            }
0745:
0746:            /**
0747:             * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
0748:             * to all registered listeners.
0749:             * 
0750:             * @param axes  the axes (<code>null</code> not permitted).
0751:             * 
0752:             * @see #setRangeAxes(ValueAxis[])
0753:             */
0754:            public void setDomainAxes(ValueAxis[] axes) {
0755:                for (int i = 0; i < axes.length; i++) {
0756:                    setDomainAxis(i, axes[i], false);
0757:                }
0758:                notifyListeners(new PlotChangeEvent(this ));
0759:            }
0760:
0761:            /**
0762:             * Returns the location of the primary domain axis.
0763:             *
0764:             * @return The location (never <code>null</code>).
0765:             * 
0766:             * @see #setDomainAxisLocation(AxisLocation)
0767:             */
0768:            public AxisLocation getDomainAxisLocation() {
0769:                return (AxisLocation) this .domainAxisLocations.get(0);
0770:            }
0771:
0772:            /**
0773:             * Sets the location of the primary domain axis and sends a 
0774:             * {@link PlotChangeEvent} to all registered listeners.
0775:             *
0776:             * @param location  the location (<code>null</code> not permitted).
0777:             * 
0778:             * @see #getDomainAxisLocation()
0779:             */
0780:            public void setDomainAxisLocation(AxisLocation location) {
0781:                // delegate...
0782:                setDomainAxisLocation(0, location, true);
0783:            }
0784:
0785:            /**
0786:             * Sets the location of the domain axis and, if requested, sends a
0787:             * {@link PlotChangeEvent} to all registered listeners.
0788:             *
0789:             * @param location  the location (<code>null</code> not permitted).
0790:             * @param notify  notify listeners?
0791:             * 
0792:             * @see #getDomainAxisLocation()
0793:             */
0794:            public void setDomainAxisLocation(AxisLocation location,
0795:                    boolean notify) {
0796:                // delegate...
0797:                setDomainAxisLocation(0, location, notify);
0798:            }
0799:
0800:            /**
0801:             * Returns the edge for the primary domain axis (taking into account the
0802:             * plot's orientation).
0803:             *
0804:             * @return The edge.
0805:             * 
0806:             * @see #getDomainAxisLocation()
0807:             * @see #getOrientation()
0808:             */
0809:            public RectangleEdge getDomainAxisEdge() {
0810:                return Plot.resolveDomainAxisLocation(getDomainAxisLocation(),
0811:                        this .orientation);
0812:            }
0813:
0814:            /**
0815:             * Returns the number of domain axes.
0816:             *
0817:             * @return The axis count.
0818:             * 
0819:             * @see #getRangeAxisCount()
0820:             */
0821:            public int getDomainAxisCount() {
0822:                return this .domainAxes.size();
0823:            }
0824:
0825:            /**
0826:             * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
0827:             * to all registered listeners.
0828:             * 
0829:             * @see #clearRangeAxes()
0830:             */
0831:            public void clearDomainAxes() {
0832:                for (int i = 0; i < this .domainAxes.size(); i++) {
0833:                    ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
0834:                    if (axis != null) {
0835:                        axis.removeChangeListener(this );
0836:                    }
0837:                }
0838:                this .domainAxes.clear();
0839:                notifyListeners(new PlotChangeEvent(this ));
0840:            }
0841:
0842:            /**
0843:             * Configures the domain axes. 
0844:             */
0845:            public void configureDomainAxes() {
0846:                for (int i = 0; i < this .domainAxes.size(); i++) {
0847:                    ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
0848:                    if (axis != null) {
0849:                        axis.configure();
0850:                    }
0851:                }
0852:            }
0853:
0854:            /**
0855:             * Returns the location for a domain axis.  If this hasn't been set
0856:             * explicitly, the method returns the location that is opposite to the
0857:             * primary domain axis location.
0858:             *
0859:             * @param index  the axis index.
0860:             *
0861:             * @return The location (never <code>null</code>).
0862:             * 
0863:             * @see #setDomainAxisLocation(int, AxisLocation)
0864:             */
0865:            public AxisLocation getDomainAxisLocation(int index) {
0866:                AxisLocation result = null;
0867:                if (index < this .domainAxisLocations.size()) {
0868:                    result = (AxisLocation) this .domainAxisLocations.get(index);
0869:                }
0870:                if (result == null) {
0871:                    result = AxisLocation.getOpposite(getDomainAxisLocation());
0872:                }
0873:                return result;
0874:            }
0875:
0876:            /**
0877:             * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
0878:             * to all registered listeners.
0879:             *
0880:             * @param index  the axis index.
0881:             * @param location  the location (<code>null</code> not permitted for index
0882:             *     0).
0883:             * 
0884:             * @see #getDomainAxisLocation(int)
0885:             */
0886:            public void setDomainAxisLocation(int index, AxisLocation location) {
0887:                // delegate...
0888:                setDomainAxisLocation(index, location, true);
0889:            }
0890:
0891:            /**
0892:             * Sets the axis location for a domain axis and, if requested, sends a
0893:             * {@link PlotChangeEvent} to all registered listeners.
0894:             * 
0895:             * @param index  the axis index.
0896:             * @param location  the location (<code>null</code> not permitted for 
0897:             *     index 0).
0898:             * @param notify  notify listeners?
0899:             * 
0900:             * @since 1.0.5
0901:             * 
0902:             * @see #getDomainAxisLocation(int)
0903:             * @see #setRangeAxisLocation(int, AxisLocation, boolean)
0904:             */
0905:            public void setDomainAxisLocation(int index, AxisLocation location,
0906:                    boolean notify) {
0907:
0908:                if (index == 0 && location == null) {
0909:                    throw new IllegalArgumentException(
0910:                            "Null 'location' for index 0 not permitted.");
0911:                }
0912:                this .domainAxisLocations.set(index, location);
0913:                if (notify) {
0914:                    notifyListeners(new PlotChangeEvent(this ));
0915:                }
0916:            }
0917:
0918:            /**
0919:             * Returns the edge for a domain axis.
0920:             *
0921:             * @param index  the axis index.
0922:             *
0923:             * @return The edge.
0924:             * 
0925:             * @see #getRangeAxisEdge(int)
0926:             */
0927:            public RectangleEdge getDomainAxisEdge(int index) {
0928:                AxisLocation location = getDomainAxisLocation(index);
0929:                RectangleEdge result = Plot.resolveDomainAxisLocation(location,
0930:                        this .orientation);
0931:                if (result == null) {
0932:                    result = RectangleEdge.opposite(getDomainAxisEdge());
0933:                }
0934:                return result;
0935:            }
0936:
0937:            /**
0938:             * Returns the range axis for the plot.  If the range axis for this plot is
0939:             * <code>null</code>, then the method will return the parent plot's range 
0940:             * axis (if there is a parent plot).
0941:             *
0942:             * @return The range axis.
0943:             * 
0944:             * @see #getRangeAxis(int)
0945:             * @see #setRangeAxis(ValueAxis)
0946:             */
0947:            public ValueAxis getRangeAxis() {
0948:                return getRangeAxis(0);
0949:            }
0950:
0951:            /**
0952:             * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
0953:             * all registered listeners.
0954:             *
0955:             * @param axis  the axis (<code>null</code> permitted).
0956:             *
0957:             * @see #getRangeAxis()
0958:             * @see #setRangeAxis(int, ValueAxis)
0959:             */
0960:            public void setRangeAxis(ValueAxis axis) {
0961:
0962:                if (axis != null) {
0963:                    axis.setPlot(this );
0964:                }
0965:
0966:                // plot is likely registered as a listener with the existing axis...
0967:                ValueAxis existing = getRangeAxis();
0968:                if (existing != null) {
0969:                    existing.removeChangeListener(this );
0970:                }
0971:
0972:                this .rangeAxes.set(0, axis);
0973:                if (axis != null) {
0974:                    axis.configure();
0975:                    axis.addChangeListener(this );
0976:                }
0977:                notifyListeners(new PlotChangeEvent(this ));
0978:
0979:            }
0980:
0981:            /**
0982:             * Returns the location of the primary range axis.
0983:             *
0984:             * @return The location (never <code>null</code>).
0985:             * 
0986:             * @see #setRangeAxisLocation(AxisLocation)
0987:             */
0988:            public AxisLocation getRangeAxisLocation() {
0989:                return (AxisLocation) this .rangeAxisLocations.get(0);
0990:            }
0991:
0992:            /**
0993:             * Sets the location of the primary range axis and sends a
0994:             * {@link PlotChangeEvent} to all registered listeners.
0995:             *
0996:             * @param location  the location (<code>null</code> not permitted).
0997:             * 
0998:             * @see #getRangeAxisLocation()
0999:             */
1000:            public void setRangeAxisLocation(AxisLocation location) {
1001:                // delegate...
1002:                setRangeAxisLocation(0, location, true);
1003:            }
1004:
1005:            /**
1006:             * Sets the location of the primary range axis and, if requested, sends a
1007:             * {@link PlotChangeEvent} to all registered listeners.
1008:             *
1009:             * @param location  the location (<code>null</code> not permitted).
1010:             * @param notify  notify listeners?
1011:             * 
1012:             * @see #getRangeAxisLocation()
1013:             */
1014:            public void setRangeAxisLocation(AxisLocation location,
1015:                    boolean notify) {
1016:                // delegate...
1017:                setRangeAxisLocation(0, location, notify);
1018:            }
1019:
1020:            /**
1021:             * Returns the edge for the primary range axis.
1022:             *
1023:             * @return The range axis edge.
1024:             * 
1025:             * @see #getRangeAxisLocation()
1026:             * @see #getOrientation()
1027:             */
1028:            public RectangleEdge getRangeAxisEdge() {
1029:                return Plot.resolveRangeAxisLocation(getRangeAxisLocation(),
1030:                        this .orientation);
1031:            }
1032:
1033:            /**
1034:             * Returns a range axis.
1035:             *
1036:             * @param index  the axis index.
1037:             *
1038:             * @return The axis (<code>null</code> possible).
1039:             * 
1040:             * @see #setRangeAxis(int, ValueAxis)
1041:             */
1042:            public ValueAxis getRangeAxis(int index) {
1043:                ValueAxis result = null;
1044:                if (index < this .rangeAxes.size()) {
1045:                    result = (ValueAxis) this .rangeAxes.get(index);
1046:                }
1047:                if (result == null) {
1048:                    Plot parent = getParent();
1049:                    if (parent instanceof  XYPlot) {
1050:                        XYPlot xy = (XYPlot) parent;
1051:                        result = xy.getRangeAxis(index);
1052:                    }
1053:                }
1054:                return result;
1055:            }
1056:
1057:            /**
1058:             * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1059:             * listeners.
1060:             *
1061:             * @param index  the axis index.
1062:             * @param axis  the axis (<code>null</code> permitted).
1063:             * 
1064:             * @see #getRangeAxis(int)
1065:             */
1066:            public void setRangeAxis(int index, ValueAxis axis) {
1067:                setRangeAxis(index, axis, true);
1068:            }
1069:
1070:            /**
1071:             * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to 
1072:             * all registered listeners.
1073:             *
1074:             * @param index  the axis index.
1075:             * @param axis  the axis (<code>null</code> permitted).
1076:             * @param notify  notify listeners?
1077:             * 
1078:             * @see #getRangeAxis(int)
1079:             */
1080:            public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1081:                ValueAxis existing = getRangeAxis(index);
1082:                if (existing != null) {
1083:                    existing.removeChangeListener(this );
1084:                }
1085:                if (axis != null) {
1086:                    axis.setPlot(this );
1087:                }
1088:                this .rangeAxes.set(index, axis);
1089:                if (axis != null) {
1090:                    axis.configure();
1091:                    axis.addChangeListener(this );
1092:                }
1093:                if (notify) {
1094:                    notifyListeners(new PlotChangeEvent(this ));
1095:                }
1096:            }
1097:
1098:            /**
1099:             * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1100:             * to all registered listeners.
1101:             * 
1102:             * @param axes  the axes (<code>null</code> not permitted).
1103:             * 
1104:             * @see #setDomainAxes(ValueAxis[])
1105:             */
1106:            public void setRangeAxes(ValueAxis[] axes) {
1107:                for (int i = 0; i < axes.length; i++) {
1108:                    setRangeAxis(i, axes[i], false);
1109:                }
1110:                notifyListeners(new PlotChangeEvent(this ));
1111:            }
1112:
1113:            /**
1114:             * Returns the number of range axes.
1115:             *
1116:             * @return The axis count.
1117:             * 
1118:             * @see #getDomainAxisCount()
1119:             */
1120:            public int getRangeAxisCount() {
1121:                return this .rangeAxes.size();
1122:            }
1123:
1124:            /**
1125:             * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1126:             * to all registered listeners.
1127:             * 
1128:             * @see #clearDomainAxes()
1129:             */
1130:            public void clearRangeAxes() {
1131:                for (int i = 0; i < this .rangeAxes.size(); i++) {
1132:                    ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1133:                    if (axis != null) {
1134:                        axis.removeChangeListener(this );
1135:                    }
1136:                }
1137:                this .rangeAxes.clear();
1138:                notifyListeners(new PlotChangeEvent(this ));
1139:            }
1140:
1141:            /**
1142:             * Configures the range axes.
1143:             * 
1144:             * @see #configureDomainAxes()
1145:             */
1146:            public void configureRangeAxes() {
1147:                for (int i = 0; i < this .rangeAxes.size(); i++) {
1148:                    ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1149:                    if (axis != null) {
1150:                        axis.configure();
1151:                    }
1152:                }
1153:            }
1154:
1155:            /**
1156:             * Returns the location for a range axis.  If this hasn't been set
1157:             * explicitly, the method returns the location that is opposite to the
1158:             * primary range axis location.
1159:             *
1160:             * @param index  the axis index.
1161:             *
1162:             * @return The location (never <code>null</code>).
1163:             * 
1164:             * @see #setRangeAxisLocation(int, AxisLocation)
1165:             */
1166:            public AxisLocation getRangeAxisLocation(int index) {
1167:                AxisLocation result = null;
1168:                if (index < this .rangeAxisLocations.size()) {
1169:                    result = (AxisLocation) this .rangeAxisLocations.get(index);
1170:                }
1171:                if (result == null) {
1172:                    result = AxisLocation.getOpposite(getRangeAxisLocation());
1173:                }
1174:                return result;
1175:            }
1176:
1177:            /**
1178:             * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1179:             * to all registered listeners.
1180:             *
1181:             * @param index  the axis index.
1182:             * @param location  the location (<code>null</code> permitted).
1183:             * 
1184:             * @see #getRangeAxisLocation(int)
1185:             */
1186:            public void setRangeAxisLocation(int index, AxisLocation location) {
1187:                // delegate...
1188:                setRangeAxisLocation(index, location, true);
1189:            }
1190:
1191:            /**
1192:             * Sets the axis location for a domain axis and, if requested, sends a
1193:             * {@link PlotChangeEvent} to all registered listeners.
1194:             * 
1195:             * @param index  the axis index.
1196:             * @param location  the location (<code>null</code> not permitted for 
1197:             *     index 0).
1198:             * @param notify  notify listeners?
1199:             * 
1200:             * @since 1.0.5
1201:             * 
1202:             * @see #getRangeAxisLocation(int)
1203:             * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1204:             */
1205:            public void setRangeAxisLocation(int index, AxisLocation location,
1206:                    boolean notify) {
1207:
1208:                if (index == 0 && location == null) {
1209:                    throw new IllegalArgumentException(
1210:                            "Null 'location' for index 0 not permitted.");
1211:                }
1212:                this .rangeAxisLocations.set(index, location);
1213:                if (notify) {
1214:                    notifyListeners(new PlotChangeEvent(this ));
1215:                }
1216:            }
1217:
1218:            /**
1219:             * Returns the edge for a range axis.
1220:             *
1221:             * @param index  the axis index.
1222:             *
1223:             * @return The edge.
1224:             * 
1225:             * @see #getRangeAxisLocation(int)
1226:             * @see #getOrientation()
1227:             */
1228:            public RectangleEdge getRangeAxisEdge(int index) {
1229:                AxisLocation location = getRangeAxisLocation(index);
1230:                RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1231:                        this .orientation);
1232:                if (result == null) {
1233:                    result = RectangleEdge.opposite(getRangeAxisEdge());
1234:                }
1235:                return result;
1236:            }
1237:
1238:            /**
1239:             * Returns the primary dataset for the plot.
1240:             *
1241:             * @return The primary dataset (possibly <code>null</code>).
1242:             * 
1243:             * @see #getDataset(int)
1244:             * @see #setDataset(XYDataset)
1245:             */
1246:            public XYDataset getDataset() {
1247:                return getDataset(0);
1248:            }
1249:
1250:            /**
1251:             * Returns a dataset.
1252:             *
1253:             * @param index  the dataset index.
1254:             *
1255:             * @return The dataset (possibly <code>null</code>).
1256:             * 
1257:             * @see #setDataset(int, XYDataset)
1258:             */
1259:            public XYDataset getDataset(int index) {
1260:                XYDataset result = null;
1261:                if (this .datasets.size() > index) {
1262:                    result = (XYDataset) this .datasets.get(index);
1263:                }
1264:                return result;
1265:            }
1266:
1267:            /**
1268:             * Sets the primary dataset for the plot, replacing the existing dataset if
1269:             * there is one.
1270:             *
1271:             * @param dataset  the dataset (<code>null</code> permitted).
1272:             * 
1273:             * @see #getDataset()
1274:             * @see #setDataset(int, XYDataset)
1275:             */
1276:            public void setDataset(XYDataset dataset) {
1277:                setDataset(0, dataset);
1278:            }
1279:
1280:            /**
1281:             * Sets a dataset for the plot.
1282:             *
1283:             * @param index  the dataset index.
1284:             * @param dataset  the dataset (<code>null</code> permitted).
1285:             * 
1286:             * @see #getDataset(int)
1287:             */
1288:            public void setDataset(int index, XYDataset dataset) {
1289:                XYDataset existing = getDataset(index);
1290:                if (existing != null) {
1291:                    existing.removeChangeListener(this );
1292:                }
1293:                this .datasets.set(index, dataset);
1294:                if (dataset != null) {
1295:                    dataset.addChangeListener(this );
1296:                }
1297:
1298:                // send a dataset change event to self...
1299:                DatasetChangeEvent event = new DatasetChangeEvent(this , dataset);
1300:                datasetChanged(event);
1301:            }
1302:
1303:            /**
1304:             * Returns the number of datasets.
1305:             *
1306:             * @return The number of datasets.
1307:             */
1308:            public int getDatasetCount() {
1309:                return this .datasets.size();
1310:            }
1311:
1312:            /**
1313:             * Returns the index of the specified dataset, or <code>-1</code> if the
1314:             * dataset does not belong to the plot.
1315:             *
1316:             * @param dataset  the dataset (<code>null</code> not permitted).
1317:             *
1318:             * @return The index.
1319:             */
1320:            public int indexOf(XYDataset dataset) {
1321:                int result = -1;
1322:                for (int i = 0; i < this .datasets.size(); i++) {
1323:                    if (dataset == this .datasets.get(i)) {
1324:                        result = i;
1325:                        break;
1326:                    }
1327:                }
1328:                return result;
1329:            }
1330:
1331:            /**
1332:             * Maps a dataset to a particular domain axis.  All data will be plotted
1333:             * against axis zero by default, no mapping is required for this case.
1334:             *
1335:             * @param index  the dataset index (zero-based).
1336:             * @param axisIndex  the axis index.
1337:             * 
1338:             * @see #mapDatasetToRangeAxis(int, int)
1339:             */
1340:            public void mapDatasetToDomainAxis(int index, int axisIndex) {
1341:                this .datasetToDomainAxisMap.put(new Integer(index),
1342:                        new Integer(axisIndex));
1343:                // fake a dataset change event to update axes...
1344:                datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1345:            }
1346:
1347:            /**
1348:             * Maps a dataset to a particular range axis.  All data will be plotted
1349:             * against axis zero by default, no mapping is required for this case.
1350:             *
1351:             * @param index  the dataset index (zero-based).
1352:             * @param axisIndex  the axis index.
1353:             * 
1354:             * @see #mapDatasetToDomainAxis(int, int)
1355:             */
1356:            public void mapDatasetToRangeAxis(int index, int axisIndex) {
1357:                this .datasetToRangeAxisMap.put(new Integer(index), new Integer(
1358:                        axisIndex));
1359:                // fake a dataset change event to update axes...
1360:                datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1361:            }
1362:
1363:            /**
1364:             * Returns the renderer for the primary dataset.
1365:             *
1366:             * @return The item renderer (possibly <code>null</code>).
1367:             * 
1368:             * @see #setRenderer(XYItemRenderer)
1369:             */
1370:            public XYItemRenderer getRenderer() {
1371:                return getRenderer(0);
1372:            }
1373:
1374:            /**
1375:             * Returns the renderer for a dataset, or <code>null</code>.
1376:             *
1377:             * @param index  the renderer index.
1378:             *
1379:             * @return The renderer (possibly <code>null</code>).
1380:             * 
1381:             * @see #setRenderer(int, XYItemRenderer)
1382:             */
1383:            public XYItemRenderer getRenderer(int index) {
1384:                XYItemRenderer result = null;
1385:                if (this .renderers.size() > index) {
1386:                    result = (XYItemRenderer) this .renderers.get(index);
1387:                }
1388:                return result;
1389:
1390:            }
1391:
1392:            /**
1393:             * Sets the renderer for the primary dataset and sends a
1394:             * {@link PlotChangeEvent} to all registered listeners.  If the renderer
1395:             * is set to <code>null</code>, no data will be displayed.
1396:             *
1397:             * @param renderer  the renderer (<code>null</code> permitted).
1398:             * 
1399:             * @see #getRenderer()
1400:             */
1401:            public void setRenderer(XYItemRenderer renderer) {
1402:                setRenderer(0, renderer);
1403:            }
1404:
1405:            /**
1406:             * Sets a renderer and sends a {@link PlotChangeEvent} to all
1407:             * registered listeners.
1408:             *
1409:             * @param index  the index.
1410:             * @param renderer  the renderer.
1411:             * 
1412:             * @see #getRenderer(int)
1413:             */
1414:            public void setRenderer(int index, XYItemRenderer renderer) {
1415:                setRenderer(index, renderer, true);
1416:            }
1417:
1418:            /**
1419:             * Sets a renderer and sends a {@link PlotChangeEvent} to all
1420:             * registered listeners.
1421:             *
1422:             * @param index  the index.
1423:             * @param renderer  the renderer.
1424:             * @param notify  notify listeners?
1425:             * 
1426:             * @see #getRenderer(int)
1427:             */
1428:            public void setRenderer(int index, XYItemRenderer renderer,
1429:                    boolean notify) {
1430:                XYItemRenderer existing = getRenderer(index);
1431:                if (existing != null) {
1432:                    existing.removeChangeListener(this );
1433:                }
1434:                this .renderers.set(index, renderer);
1435:                if (renderer != null) {
1436:                    renderer.setPlot(this );
1437:                    renderer.addChangeListener(this );
1438:                }
1439:                configureDomainAxes();
1440:                configureRangeAxes();
1441:                if (notify) {
1442:                    notifyListeners(new PlotChangeEvent(this ));
1443:                }
1444:            }
1445:
1446:            /**
1447:             * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1448:             * to all registered listeners.
1449:             * 
1450:             * @param renderers  the renderers (<code>null</code> not permitted).
1451:             */
1452:            public void setRenderers(XYItemRenderer[] renderers) {
1453:                for (int i = 0; i < renderers.length; i++) {
1454:                    setRenderer(i, renderers[i], false);
1455:                }
1456:                notifyListeners(new PlotChangeEvent(this ));
1457:            }
1458:
1459:            /**
1460:             * Returns the dataset rendering order.
1461:             *
1462:             * @return The order (never <code>null</code>).
1463:             * 
1464:             * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1465:             */
1466:            public DatasetRenderingOrder getDatasetRenderingOrder() {
1467:                return this .datasetRenderingOrder;
1468:            }
1469:
1470:            /**
1471:             * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1472:             * registered listeners.  By default, the plot renders the primary dataset
1473:             * last (so that the primary dataset overlays the secondary datasets).
1474:             * You can reverse this if you want to.
1475:             *
1476:             * @param order  the rendering order (<code>null</code> not permitted).
1477:             * 
1478:             * @see #getDatasetRenderingOrder()
1479:             */
1480:            public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1481:                if (order == null) {
1482:                    throw new IllegalArgumentException("Null 'order' argument.");
1483:                }
1484:                this .datasetRenderingOrder = order;
1485:                notifyListeners(new PlotChangeEvent(this ));
1486:            }
1487:
1488:            /**
1489:             * Returns the series rendering order.
1490:             *
1491:             * @return the order (never <code>null</code>).
1492:             * 
1493:             * @see #setSeriesRenderingOrder(SeriesRenderingOrder)
1494:             */
1495:            public SeriesRenderingOrder getSeriesRenderingOrder() {
1496:                return this .seriesRenderingOrder;
1497:            }
1498:
1499:            /**
1500:             * Sets the series order and sends a {@link PlotChangeEvent} to all
1501:             * registered listeners.  By default, the plot renders the primary series
1502:             * last (so that the primary series appears to be on top).
1503:             * You can reverse this if you want to.
1504:             *
1505:             * @param order  the rendering order (<code>null</code> not permitted).
1506:             * 
1507:             * @see #getSeriesRenderingOrder()
1508:             */
1509:            public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1510:                if (order == null) {
1511:                    throw new IllegalArgumentException("Null 'order' argument.");
1512:                }
1513:                this .seriesRenderingOrder = order;
1514:                notifyListeners(new PlotChangeEvent(this ));
1515:            }
1516:
1517:            /**
1518:             * Returns the index of the specified renderer, or <code>-1</code> if the
1519:             * renderer is not assigned to this plot.
1520:             *
1521:             * @param renderer  the renderer (<code>null</code> permitted).
1522:             *
1523:             * @return The renderer index.
1524:             */
1525:            public int getIndexOf(XYItemRenderer renderer) {
1526:                return this .renderers.indexOf(renderer);
1527:            }
1528:
1529:            /**
1530:             * Returns the renderer for the specified dataset.  The code first
1531:             * determines the index of the dataset, then checks if there is a
1532:             * renderer with the same index (if not, the method returns renderer(0).
1533:             *
1534:             * @param dataset  the dataset (<code>null</code> permitted).
1535:             *
1536:             * @return The renderer (possibly <code>null</code>).
1537:             */
1538:            public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1539:                XYItemRenderer result = null;
1540:                for (int i = 0; i < this .datasets.size(); i++) {
1541:                    if (this .datasets.get(i) == dataset) {
1542:                        result = (XYItemRenderer) this .renderers.get(i);
1543:                        if (result == null) {
1544:                            result = getRenderer();
1545:                        }
1546:                        break;
1547:                    }
1548:                }
1549:                return result;
1550:            }
1551:
1552:            /**
1553:             * Returns the weight for this plot when it is used as a subplot within a
1554:             * combined plot.
1555:             *
1556:             * @return The weight.
1557:             * 
1558:             * @see #setWeight(int)
1559:             */
1560:            public int getWeight() {
1561:                return this .weight;
1562:            }
1563:
1564:            /**
1565:             * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all
1566:             * registered listeners.
1567:             *
1568:             * @param weight  the weight.
1569:             * 
1570:             * @see #getWeight()
1571:             */
1572:            public void setWeight(int weight) {
1573:                this .weight = weight;
1574:                notifyListeners(new PlotChangeEvent(this ));
1575:            }
1576:
1577:            /**
1578:             * Returns <code>true</code> if the domain gridlines are visible, and
1579:             * <code>false<code> otherwise.
1580:             *
1581:             * @return <code>true</code> or <code>false</code>.
1582:             * 
1583:             * @see #setDomainGridlinesVisible(boolean)
1584:             */
1585:            public boolean isDomainGridlinesVisible() {
1586:                return this .domainGridlinesVisible;
1587:            }
1588:
1589:            /**
1590:             * Sets the flag that controls whether or not the domain grid-lines are
1591:             * visible.
1592:             * <p>
1593:             * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1594:             * registered listeners.
1595:             *
1596:             * @param visible  the new value of the flag.
1597:             * 
1598:             * @see #isDomainGridlinesVisible()
1599:             */
1600:            public void setDomainGridlinesVisible(boolean visible) {
1601:                if (this .domainGridlinesVisible != visible) {
1602:                    this .domainGridlinesVisible = visible;
1603:                    notifyListeners(new PlotChangeEvent(this ));
1604:                }
1605:            }
1606:
1607:            /**
1608:             * Returns the stroke for the grid-lines (if any) plotted against the
1609:             * domain axis.
1610:             *
1611:             * @return The stroke (never <code>null</code>).
1612:             * 
1613:             * @see #setDomainGridlineStroke(Stroke)
1614:             */
1615:            public Stroke getDomainGridlineStroke() {
1616:                return this .domainGridlineStroke;
1617:            }
1618:
1619:            /**
1620:             * Sets the stroke for the grid lines plotted against the domain axis, and
1621:             * sends a {@link PlotChangeEvent} to all registered listeners.
1622:             * <p>
1623:             * If you set this to <code>null</code>, no grid lines will be drawn.
1624:             *
1625:             * @param stroke  the stroke (<code>null</code> not permitted).
1626:             * 
1627:             * @throws IllegalArgumentException if <code>stroke</code> is 
1628:             *     <code>null</code>.
1629:             *
1630:             * @see #getDomainGridlineStroke()
1631:             */
1632:            public void setDomainGridlineStroke(Stroke stroke) {
1633:                if (stroke == null) {
1634:                    throw new IllegalArgumentException(
1635:                            "Null 'stroke' argument.");
1636:                }
1637:                this .domainGridlineStroke = stroke;
1638:                notifyListeners(new PlotChangeEvent(this ));
1639:            }
1640:
1641:            /**
1642:             * Returns the paint for the grid lines (if any) plotted against the domain
1643:             * axis.
1644:             *
1645:             * @return The paint (never <code>null</code>).
1646:             * 
1647:             * @see #setDomainGridlinePaint(Paint)
1648:             */
1649:            public Paint getDomainGridlinePaint() {
1650:                return this .domainGridlinePaint;
1651:            }
1652:
1653:            /**
1654:             * Sets the paint for the grid lines plotted against the domain axis, and
1655:             * sends a {@link PlotChangeEvent} to all registered listeners.
1656:             *
1657:             * @param paint  the paint (<code>null</code> not permitted).
1658:             * 
1659:             * @throws IllegalArgumentException if <code>paint</code> is 
1660:             *     <code>null</code>.
1661:             * 
1662:             * @see #getDomainGridlinePaint()
1663:             */
1664:            public void setDomainGridlinePaint(Paint paint) {
1665:                if (paint == null) {
1666:                    throw new IllegalArgumentException("Null 'paint' argument.");
1667:                }
1668:                this .domainGridlinePaint = paint;
1669:                notifyListeners(new PlotChangeEvent(this ));
1670:            }
1671:
1672:            /**
1673:             * Returns <code>true</code> if the range axis grid is visible, and
1674:             * <code>false<code> otherwise.
1675:             *
1676:             * @return A boolean.
1677:             * 
1678:             * @see #setRangeGridlinesVisible(boolean)
1679:             */
1680:            public boolean isRangeGridlinesVisible() {
1681:                return this .rangeGridlinesVisible;
1682:            }
1683:
1684:            /**
1685:             * Sets the flag that controls whether or not the range axis grid lines
1686:             * are visible.
1687:             * <p>
1688:             * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1689:             * registered listeners.
1690:             *
1691:             * @param visible  the new value of the flag.
1692:             * 
1693:             * @see #isRangeGridlinesVisible()
1694:             */
1695:            public void setRangeGridlinesVisible(boolean visible) {
1696:                if (this .rangeGridlinesVisible != visible) {
1697:                    this .rangeGridlinesVisible = visible;
1698:                    notifyListeners(new PlotChangeEvent(this ));
1699:                }
1700:            }
1701:
1702:            /**
1703:             * Returns the stroke for the grid lines (if any) plotted against the
1704:             * range axis.
1705:             *
1706:             * @return The stroke (never <code>null</code>).
1707:             * 
1708:             * @see #setRangeGridlineStroke(Stroke)
1709:             */
1710:            public Stroke getRangeGridlineStroke() {
1711:                return this .rangeGridlineStroke;
1712:            }
1713:
1714:            /**
1715:             * Sets the stroke for the grid lines plotted against the range axis,
1716:             * and sends a {@link PlotChangeEvent} to all registered listeners.
1717:             *
1718:             * @param stroke  the stroke (<code>null</code> not permitted).
1719:             * 
1720:             * @see #getRangeGridlineStroke()
1721:             */
1722:            public void setRangeGridlineStroke(Stroke stroke) {
1723:                if (stroke == null) {
1724:                    throw new IllegalArgumentException(
1725:                            "Null 'stroke' argument.");
1726:                }
1727:                this .rangeGridlineStroke = stroke;
1728:                notifyListeners(new PlotChangeEvent(this ));
1729:            }
1730:
1731:            /**
1732:             * Returns the paint for the grid lines (if any) plotted against the range
1733:             * axis.
1734:             *
1735:             * @return The paint (never <code>null</code>).
1736:             * 
1737:             * @see #setRangeGridlinePaint(Paint)
1738:             */
1739:            public Paint getRangeGridlinePaint() {
1740:                return this .rangeGridlinePaint;
1741:            }
1742:
1743:            /**
1744:             * Sets the paint for the grid lines plotted against the range axis and
1745:             * sends a {@link PlotChangeEvent} to all registered listeners.
1746:             *
1747:             * @param paint  the paint (<code>null</code> not permitted).
1748:             * 
1749:             * @see #getRangeGridlinePaint()
1750:             */
1751:            public void setRangeGridlinePaint(Paint paint) {
1752:                if (paint == null) {
1753:                    throw new IllegalArgumentException("Null 'paint' argument.");
1754:                }
1755:                this .rangeGridlinePaint = paint;
1756:                notifyListeners(new PlotChangeEvent(this ));
1757:            }
1758:
1759:            /**
1760:             * Returns a flag that controls whether or not a zero baseline is
1761:             * displayed for the domain axis.
1762:             *
1763:             * @return A boolean.
1764:             * 
1765:             * @since 1.0.5
1766:             * 
1767:             * @see #setDomainZeroBaselineVisible(boolean)
1768:             */
1769:            public boolean isDomainZeroBaselineVisible() {
1770:                return this .domainZeroBaselineVisible;
1771:            }
1772:
1773:            /**
1774:             * Sets the flag that controls whether or not the zero baseline is
1775:             * displayed for the domain axis, and sends a {@link PlotChangeEvent} to
1776:             * all registered listeners.
1777:             *
1778:             * @param visible  the flag.
1779:             * 
1780:             * @since 1.0.5
1781:             * 
1782:             * @see #isDomainZeroBaselineVisible()
1783:             */
1784:            public void setDomainZeroBaselineVisible(boolean visible) {
1785:                this .domainZeroBaselineVisible = visible;
1786:                notifyListeners(new PlotChangeEvent(this ));
1787:            }
1788:
1789:            /**
1790:             * Returns the stroke used for the zero baseline against the domain axis.
1791:             *
1792:             * @return The stroke (never <code>null</code>).
1793:             * 
1794:             * @since 1.0.5
1795:             * 
1796:             * @see #setDomainZeroBaselineStroke(Stroke)
1797:             */
1798:            public Stroke getDomainZeroBaselineStroke() {
1799:                return this .domainZeroBaselineStroke;
1800:            }
1801:
1802:            /**
1803:             * Sets the stroke for the zero baseline for the domain axis,
1804:             * and sends a {@link PlotChangeEvent} to all registered listeners.
1805:             *
1806:             * @param stroke  the stroke (<code>null</code> not permitted).
1807:             * 
1808:             * @since 1.0.5
1809:             * 
1810:             * @see #getRangeZeroBaselineStroke()
1811:             */
1812:            public void setDomainZeroBaselineStroke(Stroke stroke) {
1813:                if (stroke == null) {
1814:                    throw new IllegalArgumentException(
1815:                            "Null 'stroke' argument.");
1816:                }
1817:                this .domainZeroBaselineStroke = stroke;
1818:                notifyListeners(new PlotChangeEvent(this ));
1819:            }
1820:
1821:            /**
1822:             * Returns the paint for the zero baseline (if any) plotted against the
1823:             * domain axis.
1824:             * 
1825:             * @since 1.0.5
1826:             *
1827:             * @return The paint (never <code>null</code>).
1828:             * 
1829:             * @see #setDomainZeroBaselinePaint(Paint)
1830:             */
1831:            public Paint getDomainZeroBaselinePaint() {
1832:                return this .domainZeroBaselinePaint;
1833:            }
1834:
1835:            /**
1836:             * Sets the paint for the zero baseline plotted against the domain axis and
1837:             * sends a {@link PlotChangeEvent} to all registered listeners.
1838:             *
1839:             * @param paint  the paint (<code>null</code> not permitted).
1840:             * 
1841:             * @since 1.0.5
1842:             * 
1843:             * @see #getDomainZeroBaselinePaint()
1844:             */
1845:            public void setDomainZeroBaselinePaint(Paint paint) {
1846:                if (paint == null) {
1847:                    throw new IllegalArgumentException("Null 'paint' argument.");
1848:                }
1849:                this .domainZeroBaselinePaint = paint;
1850:                notifyListeners(new PlotChangeEvent(this ));
1851:            }
1852:
1853:            /**
1854:             * Returns a flag that controls whether or not a zero baseline is
1855:             * displayed for the range axis.
1856:             *
1857:             * @return A boolean.
1858:             * 
1859:             * @see #setRangeZeroBaselineVisible(boolean)
1860:             */
1861:            public boolean isRangeZeroBaselineVisible() {
1862:                return this .rangeZeroBaselineVisible;
1863:            }
1864:
1865:            /**
1866:             * Sets the flag that controls whether or not the zero baseline is
1867:             * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1868:             * all registered listeners.
1869:             *
1870:             * @param visible  the flag.
1871:             * 
1872:             * @see #isRangeZeroBaselineVisible()
1873:             */
1874:            public void setRangeZeroBaselineVisible(boolean visible) {
1875:                this .rangeZeroBaselineVisible = visible;
1876:                notifyListeners(new PlotChangeEvent(this ));
1877:            }
1878:
1879:            /**
1880:             * Returns the stroke used for the zero baseline against the range axis.
1881:             *
1882:             * @return The stroke (never <code>null</code>).
1883:             * 
1884:             * @see #setRangeZeroBaselineStroke(Stroke)
1885:             */
1886:            public Stroke getRangeZeroBaselineStroke() {
1887:                return this .rangeZeroBaselineStroke;
1888:            }
1889:
1890:            /**
1891:             * Sets the stroke for the zero baseline for the range axis,
1892:             * and sends a {@link PlotChangeEvent} to all registered listeners.
1893:             *
1894:             * @param stroke  the stroke (<code>null</code> not permitted).
1895:             * 
1896:             * @see #getRangeZeroBaselineStroke()
1897:             */
1898:            public void setRangeZeroBaselineStroke(Stroke stroke) {
1899:                if (stroke == null) {
1900:                    throw new IllegalArgumentException(
1901:                            "Null 'stroke' argument.");
1902:                }
1903:                this .rangeZeroBaselineStroke = stroke;
1904:                notifyListeners(new PlotChangeEvent(this ));
1905:            }
1906:
1907:            /**
1908:             * Returns the paint for the zero baseline (if any) plotted against the
1909:             * range axis.
1910:             *
1911:             * @return The paint (never <code>null</code>).
1912:             * 
1913:             * @see #setRangeZeroBaselinePaint(Paint)
1914:             */
1915:            public Paint getRangeZeroBaselinePaint() {
1916:                return this .rangeZeroBaselinePaint;
1917:            }
1918:
1919:            /**
1920:             * Sets the paint for the zero baseline plotted against the range axis and
1921:             * sends a {@link PlotChangeEvent} to all registered listeners.
1922:             *
1923:             * @param paint  the paint (<code>null</code> not permitted).
1924:             * 
1925:             * @see #getRangeZeroBaselinePaint()
1926:             */
1927:            public void setRangeZeroBaselinePaint(Paint paint) {
1928:                if (paint == null) {
1929:                    throw new IllegalArgumentException("Null 'paint' argument.");
1930:                }
1931:                this .rangeZeroBaselinePaint = paint;
1932:                notifyListeners(new PlotChangeEvent(this ));
1933:            }
1934:
1935:            /**
1936:             * Returns the paint used for the domain tick bands.  If this is
1937:             * <code>null</code>, no tick bands will be drawn.
1938:             *
1939:             * @return The paint (possibly <code>null</code>).
1940:             * 
1941:             * @see #setDomainTickBandPaint(Paint)
1942:             */
1943:            public Paint getDomainTickBandPaint() {
1944:                return this .domainTickBandPaint;
1945:            }
1946:
1947:            /**
1948:             * Sets the paint for the domain tick bands.
1949:             *
1950:             * @param paint  the paint (<code>null</code> permitted).
1951:             * 
1952:             * @see #getDomainTickBandPaint()
1953:             */
1954:            public void setDomainTickBandPaint(Paint paint) {
1955:                this .domainTickBandPaint = paint;
1956:                notifyListeners(new PlotChangeEvent(this ));
1957:            }
1958:
1959:            /**
1960:             * Returns the paint used for the range tick bands.  If this is
1961:             * <code>null</code>, no tick bands will be drawn.
1962:             *
1963:             * @return The paint (possibly <code>null</code>).
1964:             * 
1965:             * @see #setRangeTickBandPaint(Paint)
1966:             */
1967:            public Paint getRangeTickBandPaint() {
1968:                return this .rangeTickBandPaint;
1969:            }
1970:
1971:            /**
1972:             * Sets the paint for the range tick bands.
1973:             *
1974:             * @param paint  the paint (<code>null</code> permitted).
1975:             * 
1976:             * @see #getRangeTickBandPaint()
1977:             */
1978:            public void setRangeTickBandPaint(Paint paint) {
1979:                this .rangeTickBandPaint = paint;
1980:                notifyListeners(new PlotChangeEvent(this ));
1981:            }
1982:
1983:            /**
1984:             * Returns the origin for the quadrants that can be displayed on the plot.
1985:             * This defaults to (0, 0).
1986:             *
1987:             * @return The origin point (never <code>null</code>).
1988:             * 
1989:             * @see #setQuadrantOrigin(Point2D)
1990:             */
1991:            public Point2D getQuadrantOrigin() {
1992:                return this .quadrantOrigin;
1993:            }
1994:
1995:            /**
1996:             * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
1997:             * registered listeners.
1998:             *
1999:             * @param origin  the origin (<code>null</code> not permitted).
2000:             * 
2001:             * @see #getQuadrantOrigin()
2002:             */
2003:            public void setQuadrantOrigin(Point2D origin) {
2004:                if (origin == null) {
2005:                    throw new IllegalArgumentException(
2006:                            "Null 'origin' argument.");
2007:                }
2008:                this .quadrantOrigin = origin;
2009:                notifyListeners(new PlotChangeEvent(this ));
2010:            }
2011:
2012:            /**
2013:             * Returns the paint used for the specified quadrant.
2014:             *
2015:             * @param index  the quadrant index (0-3).
2016:             *
2017:             * @return The paint (possibly <code>null</code>).
2018:             * 
2019:             * @see #setQuadrantPaint(int, Paint)
2020:             */
2021:            public Paint getQuadrantPaint(int index) {
2022:                if (index < 0 || index > 3) {
2023:                    throw new IllegalArgumentException(
2024:                            "The index should be in the range 0 to 3.");
2025:                }
2026:                return this .quadrantPaint[index];
2027:            }
2028:
2029:            /**
2030:             * Sets the paint used for the specified quadrant and sends a
2031:             * {@link PlotChangeEvent} to all registered listeners.
2032:             *
2033:             * @param index  the quadrant index (0-3).
2034:             * @param paint  the paint (<code>null</code> permitted).
2035:             * 
2036:             * @see #getQuadrantPaint(int)
2037:             */
2038:            public void setQuadrantPaint(int index, Paint paint) {
2039:                if (index < 0 || index > 3) {
2040:                    throw new IllegalArgumentException(
2041:                            "The index should be in the range 0 to 3.");
2042:                }
2043:                this .quadrantPaint[index] = paint;
2044:                notifyListeners(new PlotChangeEvent(this ));
2045:            }
2046:
2047:            /**
2048:             * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
2049:             * to all registered listeners.
2050:             * <P>
2051:             * Typically a marker will be drawn by the renderer as a line perpendicular
2052:             * to the range axis, however this is entirely up to the renderer.
2053:             *
2054:             * @param marker  the marker (<code>null</code> not permitted).
2055:             * 
2056:             * @see #addDomainMarker(Marker, Layer)
2057:             * @see #clearDomainMarkers()
2058:             */
2059:            public void addDomainMarker(Marker marker) {
2060:                // defer argument checking...
2061:                addDomainMarker(marker, Layer.FOREGROUND);
2062:            }
2063:
2064:            /**
2065:             * Adds a marker for the domain axis in the specified layer and sends a
2066:             * {@link PlotChangeEvent} to all registered listeners.
2067:             * <P>
2068:             * Typically a marker will be drawn by the renderer as a line perpendicular
2069:             * to the range axis, however this is entirely up to the renderer.
2070:             *
2071:             * @param marker  the marker (<code>null</code> not permitted).
2072:             * @param layer  the layer (foreground or background).
2073:             * 
2074:             * @see #addDomainMarker(int, Marker, Layer)
2075:             */
2076:            public void addDomainMarker(Marker marker, Layer layer) {
2077:                addDomainMarker(0, marker, layer);
2078:            }
2079:
2080:            /**
2081:             * Clears all the (foreground and background) domain markers and sends a
2082:             * {@link PlotChangeEvent} to all registered listeners.
2083:             * 
2084:             * @see #addDomainMarker(int, Marker, Layer)
2085:             */
2086:            public void clearDomainMarkers() {
2087:                if (this .backgroundDomainMarkers != null) {
2088:                    Set keys = this .backgroundDomainMarkers.keySet();
2089:                    Iterator iterator = keys.iterator();
2090:                    while (iterator.hasNext()) {
2091:                        Integer key = (Integer) iterator.next();
2092:                        clearDomainMarkers(key.intValue());
2093:                    }
2094:                    this .backgroundDomainMarkers.clear();
2095:                }
2096:                if (this .foregroundDomainMarkers != null) {
2097:                    Set keys = this .foregroundDomainMarkers.keySet();
2098:                    Iterator iterator = keys.iterator();
2099:                    while (iterator.hasNext()) {
2100:                        Integer key = (Integer) iterator.next();
2101:                        clearDomainMarkers(key.intValue());
2102:                    }
2103:                    this .foregroundDomainMarkers.clear();
2104:                }
2105:                notifyListeners(new PlotChangeEvent(this ));
2106:            }
2107:
2108:            /**
2109:             * Clears the (foreground and background) domain markers for a particular
2110:             * renderer.
2111:             *
2112:             * @param index  the renderer index.
2113:             * 
2114:             * @see #clearRangeMarkers(int)
2115:             */
2116:            public void clearDomainMarkers(int index) {
2117:                Integer key = new Integer(index);
2118:                if (this .backgroundDomainMarkers != null) {
2119:                    Collection markers = (Collection) this .backgroundDomainMarkers
2120:                            .get(key);
2121:                    if (markers != null) {
2122:                        Iterator iterator = markers.iterator();
2123:                        while (iterator.hasNext()) {
2124:                            Marker m = (Marker) iterator.next();
2125:                            m.removeChangeListener(this );
2126:                        }
2127:                        markers.clear();
2128:                    }
2129:                }
2130:                if (this .foregroundRangeMarkers != null) {
2131:                    Collection markers = (Collection) this .foregroundDomainMarkers
2132:                            .get(key);
2133:                    if (markers != null) {
2134:                        Iterator iterator = markers.iterator();
2135:                        while (iterator.hasNext()) {
2136:                            Marker m = (Marker) iterator.next();
2137:                            m.removeChangeListener(this );
2138:                        }
2139:                        markers.clear();
2140:                    }
2141:                }
2142:                notifyListeners(new PlotChangeEvent(this ));
2143:            }
2144:
2145:            /**
2146:             * Adds a marker for a specific dataset/renderer and sends a 
2147:             * {@link PlotChangeEvent} to all registered listeners.
2148:             * <P>
2149:             * Typically a marker will be drawn by the renderer as a line perpendicular
2150:             * to the domain axis (that the renderer is mapped to), however this is
2151:             * entirely up to the renderer.
2152:             *
2153:             * @param index  the dataset/renderer index.
2154:             * @param marker  the marker.
2155:             * @param layer  the layer (foreground or background).
2156:             * 
2157:             * @see #clearDomainMarkers(int)
2158:             * @see #addRangeMarker(int, Marker, Layer)
2159:             */
2160:            public void addDomainMarker(int index, Marker marker, Layer layer) {
2161:                if (marker == null) {
2162:                    throw new IllegalArgumentException(
2163:                            "Null 'marker' not permitted.");
2164:                }
2165:                if (layer == null) {
2166:                    throw new IllegalArgumentException(
2167:                            "Null 'layer' not permitted.");
2168:                }
2169:                Collection markers;
2170:                if (layer == Layer.FOREGROUND) {
2171:                    markers = (Collection) this .foregroundDomainMarkers
2172:                            .get(new Integer(index));
2173:                    if (markers == null) {
2174:                        markers = new java.util.ArrayList();
2175:                        this .foregroundDomainMarkers.put(new Integer(index),
2176:                                markers);
2177:                    }
2178:                    markers.add(marker);
2179:                } else if (layer == Layer.BACKGROUND) {
2180:                    markers = (Collection) this .backgroundDomainMarkers
2181:                            .get(new Integer(index));
2182:                    if (markers == null) {
2183:                        markers = new java.util.ArrayList();
2184:                        this .backgroundDomainMarkers.put(new Integer(index),
2185:                                markers);
2186:                    }
2187:                    markers.add(marker);
2188:                }
2189:                marker.addChangeListener(this );
2190:                notifyListeners(new PlotChangeEvent(this ));
2191:            }
2192:
2193:            /**
2194:             * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
2195:             * all registered listeners.
2196:             * <P>
2197:             * Typically a marker will be drawn by the renderer as a line perpendicular
2198:             * to the range axis, however this is entirely up to the renderer.
2199:             *
2200:             * @param marker  the marker (<code>null</code> not permitted).
2201:             * 
2202:             * @see #addRangeMarker(Marker, Layer)
2203:             */
2204:            public void addRangeMarker(Marker marker) {
2205:                addRangeMarker(marker, Layer.FOREGROUND);
2206:            }
2207:
2208:            /**
2209:             * Adds a marker for the range axis in the specified layer and sends a
2210:             * {@link PlotChangeEvent} to all registered listeners.
2211:             * <P>
2212:             * Typically a marker will be drawn by the renderer as a line perpendicular
2213:             * to the range axis, however this is entirely up to the renderer.
2214:             *
2215:             * @param marker  the marker (<code>null</code> not permitted).
2216:             * @param layer  the layer (foreground or background).
2217:             * 
2218:             * @see #addRangeMarker(int, Marker, Layer)
2219:             */
2220:            public void addRangeMarker(Marker marker, Layer layer) {
2221:                addRangeMarker(0, marker, layer);
2222:            }
2223:
2224:            /**
2225:             * Clears all the range markers and sends a {@link PlotChangeEvent} to all
2226:             * registered listeners.
2227:             * 
2228:             * @see #clearRangeMarkers()
2229:             */
2230:            public void clearRangeMarkers() {
2231:                if (this .backgroundRangeMarkers != null) {
2232:                    Set keys = this .backgroundRangeMarkers.keySet();
2233:                    Iterator iterator = keys.iterator();
2234:                    while (iterator.hasNext()) {
2235:                        Integer key = (Integer) iterator.next();
2236:                        clearRangeMarkers(key.intValue());
2237:                    }
2238:                    this .backgroundRangeMarkers.clear();
2239:                }
2240:                if (this .foregroundRangeMarkers != null) {
2241:                    Set keys = this .foregroundRangeMarkers.keySet();
2242:                    Iterator iterator = keys.iterator();
2243:                    while (iterator.hasNext()) {
2244:                        Integer key = (Integer) iterator.next();
2245:                        clearRangeMarkers(key.intValue());
2246:                    }
2247:                    this .foregroundRangeMarkers.clear();
2248:                }
2249:                notifyListeners(new PlotChangeEvent(this ));
2250:            }
2251:
2252:            /**
2253:             * Adds a marker for a specific dataset/renderer and sends a 
2254:             * {@link PlotChangeEvent} to all registered listeners.
2255:             * <P>
2256:             * Typically a marker will be drawn by the renderer as a line perpendicular
2257:             * to the range axis, however this is entirely up to the renderer.
2258:             *
2259:             * @param index  the dataset/renderer index.
2260:             * @param marker  the marker.
2261:             * @param layer  the layer (foreground or background).
2262:             * 
2263:             * @see #clearRangeMarkers(int)
2264:             * @see #addDomainMarker(int, Marker, Layer)
2265:             */
2266:            public void addRangeMarker(int index, Marker marker, Layer layer) {
2267:                Collection markers;
2268:                if (layer == Layer.FOREGROUND) {
2269:                    markers = (Collection) this .foregroundRangeMarkers
2270:                            .get(new Integer(index));
2271:                    if (markers == null) {
2272:                        markers = new java.util.ArrayList();
2273:                        this .foregroundRangeMarkers.put(new Integer(index),
2274:                                markers);
2275:                    }
2276:                    markers.add(marker);
2277:                } else if (layer == Layer.BACKGROUND) {
2278:                    markers = (Collection) this .backgroundRangeMarkers
2279:                            .get(new Integer(index));
2280:                    if (markers == null) {
2281:                        markers = new java.util.ArrayList();
2282:                        this .backgroundRangeMarkers.put(new Integer(index),
2283:                                markers);
2284:                    }
2285:                    markers.add(marker);
2286:                }
2287:                marker.addChangeListener(this );
2288:                notifyListeners(new PlotChangeEvent(this ));
2289:            }
2290:
2291:            /**
2292:             * Clears the (foreground and background) range markers for a particular
2293:             * renderer.
2294:             *
2295:             * @param index  the renderer index.
2296:             */
2297:            public void clearRangeMarkers(int index) {
2298:                Integer key = new Integer(index);
2299:                if (this .backgroundRangeMarkers != null) {
2300:                    Collection markers = (Collection) this .backgroundRangeMarkers
2301:                            .get(key);
2302:                    if (markers != null) {
2303:                        Iterator iterator = markers.iterator();
2304:                        while (iterator.hasNext()) {
2305:                            Marker m = (Marker) iterator.next();
2306:                            m.removeChangeListener(this );
2307:                        }
2308:                        markers.clear();
2309:                    }
2310:                }
2311:                if (this .foregroundRangeMarkers != null) {
2312:                    Collection markers = (Collection) this .foregroundRangeMarkers
2313:                            .get(key);
2314:                    if (markers != null) {
2315:                        Iterator iterator = markers.iterator();
2316:                        while (iterator.hasNext()) {
2317:                            Marker m = (Marker) iterator.next();
2318:                            m.removeChangeListener(this );
2319:                        }
2320:                        markers.clear();
2321:                    }
2322:                }
2323:                notifyListeners(new PlotChangeEvent(this ));
2324:            }
2325:
2326:            /**
2327:             * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to 
2328:             * all registered listeners.
2329:             *
2330:             * @param annotation  the annotation (<code>null</code> not permitted).
2331:             * 
2332:             * @see #getAnnotations()
2333:             * @see #removeAnnotation(XYAnnotation)
2334:             */
2335:            public void addAnnotation(XYAnnotation annotation) {
2336:                if (annotation == null) {
2337:                    throw new IllegalArgumentException(
2338:                            "Null 'annotation' argument.");
2339:                }
2340:                this .annotations.add(annotation);
2341:                notifyListeners(new PlotChangeEvent(this ));
2342:            }
2343:
2344:            /**
2345:             * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2346:             * to all registered listeners.
2347:             *
2348:             * @param annotation  the annotation (<code>null</code> not permitted).
2349:             *
2350:             * @return A boolean (indicates whether or not the annotation was removed).
2351:             * 
2352:             * @see #addAnnotation(XYAnnotation)
2353:             * @see #getAnnotations()
2354:             */
2355:            public boolean removeAnnotation(XYAnnotation annotation) {
2356:                if (annotation == null) {
2357:                    throw new IllegalArgumentException(
2358:                            "Null 'annotation' argument.");
2359:                }
2360:                boolean removed = this .annotations.remove(annotation);
2361:                if (removed) {
2362:                    notifyListeners(new PlotChangeEvent(this ));
2363:                }
2364:                return removed;
2365:            }
2366:
2367:            /**
2368:             * Returns the list of annotations.
2369:             *
2370:             * @return The list of annotations.
2371:             * 
2372:             * @since 1.0.1
2373:             * 
2374:             * @see #addAnnotation(XYAnnotation)
2375:             */
2376:            public List getAnnotations() {
2377:                return new ArrayList(this .annotations);
2378:            }
2379:
2380:            /**
2381:             * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2382:             * registered listeners.
2383:             * 
2384:             * @see #addAnnotation(XYAnnotation)
2385:             */
2386:            public void clearAnnotations() {
2387:                this .annotations.clear();
2388:                notifyListeners(new PlotChangeEvent(this ));
2389:            }
2390:
2391:            /**
2392:             * Calculates the space required for all the axes in the plot.
2393:             *
2394:             * @param g2  the graphics device.
2395:             * @param plotArea  the plot area.
2396:             *
2397:             * @return The required space.
2398:             */
2399:            protected AxisSpace calculateAxisSpace(Graphics2D g2,
2400:                    Rectangle2D plotArea) {
2401:                AxisSpace space = new AxisSpace();
2402:                space = calculateDomainAxisSpace(g2, plotArea, space);
2403:                space = calculateRangeAxisSpace(g2, plotArea, space);
2404:                return space;
2405:            }
2406:
2407:            /**
2408:             * Calculates the space required for the domain axis/axes.
2409:             *
2410:             * @param g2  the graphics device.
2411:             * @param plotArea  the plot area.
2412:             * @param space  a carrier for the result (<code>null</code> permitted).
2413:             *
2414:             * @return The required space.
2415:             */
2416:            protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
2417:                    Rectangle2D plotArea, AxisSpace space) {
2418:
2419:                if (space == null) {
2420:                    space = new AxisSpace();
2421:                }
2422:
2423:                // reserve some space for the domain axis...
2424:                if (this .fixedDomainAxisSpace != null) {
2425:                    if (this .orientation == PlotOrientation.HORIZONTAL) {
2426:                        space.ensureAtLeast(
2427:                                this .fixedDomainAxisSpace.getLeft(),
2428:                                RectangleEdge.LEFT);
2429:                        space.ensureAtLeast(this .fixedDomainAxisSpace
2430:                                .getRight(), RectangleEdge.RIGHT);
2431:                    } else if (this .orientation == PlotOrientation.VERTICAL) {
2432:                        space.ensureAtLeast(this .fixedDomainAxisSpace.getTop(),
2433:                                RectangleEdge.TOP);
2434:                        space.ensureAtLeast(this .fixedDomainAxisSpace
2435:                                .getBottom(), RectangleEdge.BOTTOM);
2436:                    }
2437:                } else {
2438:                    // reserve space for the domain axes...
2439:                    for (int i = 0; i < this .domainAxes.size(); i++) {
2440:                        Axis axis = (Axis) this .domainAxes.get(i);
2441:                        if (axis != null) {
2442:                            RectangleEdge edge = getDomainAxisEdge(i);
2443:                            space = axis.reserveSpace(g2, this , plotArea, edge,
2444:                                    space);
2445:                        }
2446:                    }
2447:                }
2448:
2449:                return space;
2450:
2451:            }
2452:
2453:            /**
2454:             * Calculates the space required for the range axis/axes.
2455:             *
2456:             * @param g2  the graphics device.
2457:             * @param plotArea  the plot area.
2458:             * @param space  a carrier for the result (<code>null</code> permitted).
2459:             *
2460:             * @return The required space.
2461:             */
2462:            protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
2463:                    Rectangle2D plotArea, AxisSpace space) {
2464:
2465:                if (space == null) {
2466:                    space = new AxisSpace();
2467:                }
2468:
2469:                // reserve some space for the range axis...
2470:                if (this .fixedRangeAxisSpace != null) {
2471:                    if (this .orientation == PlotOrientation.HORIZONTAL) {
2472:                        space.ensureAtLeast(this .fixedRangeAxisSpace.getTop(),
2473:                                RectangleEdge.TOP);
2474:                        space.ensureAtLeast(this .fixedRangeAxisSpace
2475:                                .getBottom(), RectangleEdge.BOTTOM);
2476:                    } else if (this .orientation == PlotOrientation.VERTICAL) {
2477:                        space.ensureAtLeast(this .fixedRangeAxisSpace.getLeft(),
2478:                                RectangleEdge.LEFT);
2479:                        space.ensureAtLeast(
2480:                                this .fixedRangeAxisSpace.getRight(),
2481:                                RectangleEdge.RIGHT);
2482:                    }
2483:                } else {
2484:                    // reserve space for the range axes...
2485:                    for (int i = 0; i < this .rangeAxes.size(); i++) {
2486:                        Axis axis = (Axis) this .rangeAxes.get(i);
2487:                        if (axis != null) {
2488:                            RectangleEdge edge = getRangeAxisEdge(i);
2489:                            space = axis.reserveSpace(g2, this , plotArea, edge,
2490:                                    space);
2491:                        }
2492:                    }
2493:                }
2494:                return space;
2495:
2496:            }
2497:
2498:            /**
2499:             * Draws the plot within the specified area on a graphics device.
2500:             *
2501:             * @param g2  the graphics device.
2502:             * @param area  the plot area (in Java2D space).
2503:             * @param anchor  an anchor point in Java2D space (<code>null</code>
2504:             *                permitted).
2505:             * @param parentState  the state from the parent plot, if there is one
2506:             *                     (<code>null</code> permitted).
2507:             * @param info  collects chart drawing information (<code>null</code>
2508:             *              permitted).
2509:             */
2510:            public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2511:                    PlotState parentState, PlotRenderingInfo info) {
2512:
2513:                // if the plot area is too small, just return...
2514:                boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2515:                boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2516:                if (b1 || b2) {
2517:                    return;
2518:                }
2519:
2520:                // record the plot area...
2521:                if (info != null) {
2522:                    info.setPlotArea(area);
2523:                }
2524:
2525:                // adjust the drawing area for the plot insets (if any)...
2526:                RectangleInsets insets = getInsets();
2527:                insets.trim(area);
2528:
2529:                AxisSpace space = calculateAxisSpace(g2, area);
2530:                Rectangle2D dataArea = space.shrink(area, null);
2531:                this .axisOffset.trim(dataArea);
2532:
2533:                if (info != null) {
2534:                    info.setDataArea(dataArea);
2535:                }
2536:
2537:                // draw the plot background and axes...
2538:                drawBackground(g2, dataArea);
2539:                Map axisStateMap = drawAxes(g2, area, dataArea, info);
2540:
2541:                PlotOrientation orient = getOrientation();
2542:
2543:                // the anchor point is typically the point where the mouse last
2544:                // clicked - the crosshairs will be driven off this point...
2545:                if (anchor != null && !dataArea.contains(anchor)) {
2546:                    anchor = null;
2547:                }
2548:                CrosshairState crosshairState = new CrosshairState();
2549:                crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
2550:                crosshairState.setAnchor(anchor);
2551:
2552:                crosshairState.setAnchorX(Double.NaN);
2553:                crosshairState.setAnchorY(Double.NaN);
2554:                if (anchor != null) {
2555:                    ValueAxis domainAxis = getDomainAxis();
2556:                    if (domainAxis != null) {
2557:                        double x;
2558:                        if (orient == PlotOrientation.VERTICAL) {
2559:                            x = domainAxis.java2DToValue(anchor.getX(),
2560:                                    dataArea, getDomainAxisEdge());
2561:                        } else {
2562:                            x = domainAxis.java2DToValue(anchor.getY(),
2563:                                    dataArea, getDomainAxisEdge());
2564:                        }
2565:                        crosshairState.setAnchorX(x);
2566:                    }
2567:                    ValueAxis rangeAxis = getRangeAxis();
2568:                    if (rangeAxis != null) {
2569:                        double y;
2570:                        if (orient == PlotOrientation.VERTICAL) {
2571:                            y = rangeAxis.java2DToValue(anchor.getY(),
2572:                                    dataArea, getRangeAxisEdge());
2573:                        } else {
2574:                            y = rangeAxis.java2DToValue(anchor.getX(),
2575:                                    dataArea, getRangeAxisEdge());
2576:                        }
2577:                        crosshairState.setAnchorY(y);
2578:                    }
2579:                }
2580:                crosshairState.setCrosshairX(getDomainCrosshairValue());
2581:                crosshairState.setCrosshairY(getRangeCrosshairValue());
2582:                Shape originalClip = g2.getClip();
2583:                Composite originalComposite = g2.getComposite();
2584:
2585:                g2.clip(dataArea);
2586:                g2.setComposite(AlphaComposite.getInstance(
2587:                        AlphaComposite.SRC_OVER, getForegroundAlpha()));
2588:
2589:                AxisState domainAxisState = (AxisState) axisStateMap
2590:                        .get(getDomainAxis());
2591:                if (domainAxisState == null) {
2592:                    if (parentState != null) {
2593:                        domainAxisState = (AxisState) parentState
2594:                                .getSharedAxisStates().get(getDomainAxis());
2595:                    }
2596:                }
2597:
2598:                AxisState rangeAxisState = (AxisState) axisStateMap
2599:                        .get(getRangeAxis());
2600:                if (rangeAxisState == null) {
2601:                    if (parentState != null) {
2602:                        rangeAxisState = (AxisState) parentState
2603:                                .getSharedAxisStates().get(getRangeAxis());
2604:                    }
2605:                }
2606:                if (domainAxisState != null) {
2607:                    drawDomainTickBands(g2, dataArea, domainAxisState
2608:                            .getTicks());
2609:                }
2610:                if (rangeAxisState != null) {
2611:                    drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks());
2612:                }
2613:                if (domainAxisState != null) {
2614:                    drawDomainGridlines(g2, dataArea, domainAxisState
2615:                            .getTicks());
2616:                    drawZeroDomainBaseline(g2, dataArea);
2617:                }
2618:                if (rangeAxisState != null) {
2619:                    drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2620:                    drawZeroRangeBaseline(g2, dataArea);
2621:                }
2622:
2623:                // draw the markers that are associated with a specific renderer...
2624:                for (int i = 0; i < this .renderers.size(); i++) {
2625:                    drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2626:                }
2627:                for (int i = 0; i < this .renderers.size(); i++) {
2628:                    drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2629:                }
2630:
2631:                // now draw annotations and render data items...
2632:                boolean foundData = false;
2633:                DatasetRenderingOrder order = getDatasetRenderingOrder();
2634:                if (order == DatasetRenderingOrder.FORWARD) {
2635:
2636:                    // draw background annotations
2637:                    int rendererCount = this .renderers.size();
2638:                    for (int i = 0; i < rendererCount; i++) {
2639:                        XYItemRenderer r = getRenderer(i);
2640:                        if (r != null) {
2641:                            ValueAxis domainAxis = getDomainAxisForDataset(i);
2642:                            ValueAxis rangeAxis = getRangeAxisForDataset(i);
2643:                            r.drawAnnotations(g2, dataArea, domainAxis,
2644:                                    rangeAxis, Layer.BACKGROUND, info);
2645:                        }
2646:                    }
2647:
2648:                    // render data items...
2649:                    for (int i = 0; i < getDatasetCount(); i++) {
2650:                        foundData = render(g2, dataArea, i, info,
2651:                                crosshairState)
2652:                                || foundData;
2653:                    }
2654:
2655:                    // draw foreground annotations
2656:                    for (int i = 0; i < rendererCount; i++) {
2657:                        XYItemRenderer r = getRenderer(i);
2658:                        if (r != null) {
2659:                            ValueAxis domainAxis = getDomainAxisForDataset(i);
2660:                            ValueAxis rangeAxis = getRangeAxisForDataset(i);
2661:                            r.drawAnnotations(g2, dataArea, domainAxis,
2662:                                    rangeAxis, Layer.FOREGROUND, info);
2663:                        }
2664:                    }
2665:
2666:                } else if (order == DatasetRenderingOrder.REVERSE) {
2667:
2668:                    // draw background annotations
2669:                    int rendererCount = this .renderers.size();
2670:                    for (int i = rendererCount - 1; i >= 0; i--) {
2671:                        XYItemRenderer r = getRenderer(i);
2672:                        if (i >= getDatasetCount()) { // we need the dataset to make
2673:                            continue; // a link to the axes
2674:                        }
2675:                        if (r != null) {
2676:                            ValueAxis domainAxis = getDomainAxisForDataset(i);
2677:                            ValueAxis rangeAxis = getRangeAxisForDataset(i);
2678:                            r.drawAnnotations(g2, dataArea, domainAxis,
2679:                                    rangeAxis, Layer.BACKGROUND, info);
2680:                        }
2681:                    }
2682:
2683:                    for (int i = getDatasetCount() - 1; i >= 0; i--) {
2684:                        foundData = render(g2, dataArea, i, info,
2685:                                crosshairState)
2686:                                || foundData;
2687:                    }
2688:
2689:                    // draw foreground annotations
2690:                    for (int i = rendererCount - 1; i >= 0; i--) {
2691:                        XYItemRenderer r = getRenderer(i);
2692:                        if (i >= getDatasetCount()) { // we need the dataset to make
2693:                            continue; // a link to the axes
2694:                        }
2695:                        if (r != null) {
2696:                            ValueAxis domainAxis = getDomainAxisForDataset(i);
2697:                            ValueAxis rangeAxis = getRangeAxisForDataset(i);
2698:                            r.drawAnnotations(g2, dataArea, domainAxis,
2699:                                    rangeAxis, Layer.FOREGROUND, info);
2700:                        }
2701:                    }
2702:
2703:                }
2704:
2705:                // draw domain crosshair if required...
2706:                int xAxisIndex = crosshairState.getDomainAxisIndex();
2707:                ValueAxis xAxis = getDomainAxis(xAxisIndex);
2708:                RectangleEdge xAxisEdge = getDomainAxisEdge(xAxisIndex);
2709:                if (!this .domainCrosshairLockedOnData && anchor != null) {
2710:                    double xx;
2711:                    if (orient == PlotOrientation.VERTICAL) {
2712:                        xx = xAxis.java2DToValue(anchor.getX(), dataArea,
2713:                                xAxisEdge);
2714:                    } else {
2715:                        xx = xAxis.java2DToValue(anchor.getY(), dataArea,
2716:                                xAxisEdge);
2717:                    }
2718:                    crosshairState.setCrosshairX(xx);
2719:                }
2720:                setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
2721:                if (isDomainCrosshairVisible()) {
2722:                    double x = getDomainCrosshairValue();
2723:                    Paint paint = getDomainCrosshairPaint();
2724:                    Stroke stroke = getDomainCrosshairStroke();
2725:                    drawDomainCrosshair(g2, dataArea, orient, x, xAxis, stroke,
2726:                            paint);
2727:                }
2728:
2729:                // draw range crosshair if required...
2730:                int yAxisIndex = crosshairState.getRangeAxisIndex();
2731:                ValueAxis yAxis = getRangeAxis(yAxisIndex);
2732:                RectangleEdge yAxisEdge = getRangeAxisEdge(yAxisIndex);
2733:                if (!this .rangeCrosshairLockedOnData && anchor != null) {
2734:                    double yy;
2735:                    if (orient == PlotOrientation.VERTICAL) {
2736:                        yy = yAxis.java2DToValue(anchor.getY(), dataArea,
2737:                                yAxisEdge);
2738:                    } else {
2739:                        yy = yAxis.java2DToValue(anchor.getX(), dataArea,
2740:                                yAxisEdge);
2741:                    }
2742:                    crosshairState.setCrosshairY(yy);
2743:                }
2744:                setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
2745:                if (isRangeCrosshairVisible()) {
2746:                    double y = getRangeCrosshairValue();
2747:                    Paint paint = getRangeCrosshairPaint();
2748:                    Stroke stroke = getRangeCrosshairStroke();
2749:                    drawRangeCrosshair(g2, dataArea, orient, y, yAxis, stroke,
2750:                            paint);
2751:                }
2752:
2753:                if (!foundData) {
2754:                    drawNoDataMessage(g2, dataArea);
2755:                }
2756:
2757:                for (int i = 0; i < this .renderers.size(); i++) {
2758:                    drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2759:                }
2760:                for (int i = 0; i < this .renderers.size(); i++) {
2761:                    drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2762:                }
2763:
2764:                drawAnnotations(g2, dataArea, info);
2765:                g2.setClip(originalClip);
2766:                g2.setComposite(originalComposite);
2767:
2768:                drawOutline(g2, dataArea);
2769:
2770:            }
2771:
2772:            /**
2773:             * Draws the background for the plot.
2774:             *
2775:             * @param g2  the graphics device.
2776:             * @param area  the area.
2777:             */
2778:            public void drawBackground(Graphics2D g2, Rectangle2D area) {
2779:                fillBackground(g2, area, this .orientation);
2780:                drawQuadrants(g2, area);
2781:                drawBackgroundImage(g2, area);
2782:            }
2783:
2784:            /**
2785:             * Draws the quadrants.
2786:             *
2787:             * @param g2  the graphics device.
2788:             * @param area  the area.
2789:             * 
2790:             * @see #setQuadrantOrigin(Point2D)
2791:             * @see #setQuadrantPaint(int, Paint)
2792:             */
2793:            protected void drawQuadrants(Graphics2D g2, Rectangle2D area) {
2794:                //  0 | 1
2795:                //  --+--
2796:                //  2 | 3
2797:                boolean somethingToDraw = false;
2798:
2799:                ValueAxis xAxis = getDomainAxis();
2800:                double x = this .quadrantOrigin.getX();
2801:                double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge());
2802:
2803:                ValueAxis yAxis = getRangeAxis();
2804:                double y = this .quadrantOrigin.getY();
2805:                double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge());
2806:
2807:                double xmin = xAxis.getLowerBound();
2808:                double xxmin = xAxis.valueToJava2D(xmin, area,
2809:                        getDomainAxisEdge());
2810:
2811:                double xmax = xAxis.getUpperBound();
2812:                double xxmax = xAxis.valueToJava2D(xmax, area,
2813:                        getDomainAxisEdge());
2814:
2815:                double ymin = yAxis.getLowerBound();
2816:                double yymin = yAxis.valueToJava2D(ymin, area,
2817:                        getRangeAxisEdge());
2818:
2819:                double ymax = yAxis.getUpperBound();
2820:                double yymax = yAxis.valueToJava2D(ymax, area,
2821:                        getRangeAxisEdge());
2822:
2823:                Rectangle2D[] r = new Rectangle2D[] { null, null, null, null };
2824:                if (this .quadrantPaint[0] != null) {
2825:                    if (x > xmin && y < ymax) {
2826:                        if (this .orientation == PlotOrientation.HORIZONTAL) {
2827:                            r[0] = new Rectangle2D.Double(Math.min(yymax, yy),
2828:                                    Math.min(xxmin, xx), Math.abs(yy - yymax),
2829:                                    Math.abs(xx - xxmin));
2830:                        } else { // PlotOrientation.VERTICAL
2831:                            r[0] = new Rectangle2D.Double(Math.min(xxmin, xx),
2832:                                    Math.min(yymax, yy), Math.abs(xx - xxmin),
2833:                                    Math.abs(yy - yymax));
2834:                        }
2835:                        somethingToDraw = true;
2836:                    }
2837:                }
2838:                if (this .quadrantPaint[1] != null) {
2839:                    if (x < xmax && y < ymax) {
2840:                        if (this .orientation == PlotOrientation.HORIZONTAL) {
2841:                            r[1] = new Rectangle2D.Double(Math.min(yymax, yy),
2842:                                    Math.min(xxmax, xx), Math.abs(yy - yymax),
2843:                                    Math.abs(xx - xxmax));
2844:                        } else { // PlotOrientation.VERTICAL
2845:                            r[1] = new Rectangle2D.Double(Math.min(xx, xxmax),
2846:                                    Math.min(yymax, yy), Math.abs(xx - xxmax),
2847:                                    Math.abs(yy - yymax));
2848:                        }
2849:                        somethingToDraw = true;
2850:                    }
2851:                }
2852:                if (this .quadrantPaint[2] != null) {
2853:                    if (x > xmin && y > ymin) {
2854:                        if (this .orientation == PlotOrientation.HORIZONTAL) {
2855:                            r[2] = new Rectangle2D.Double(Math.min(yymin, yy),
2856:                                    Math.min(xxmin, xx), Math.abs(yy - yymin),
2857:                                    Math.abs(xx - xxmin));
2858:                        } else { // PlotOrientation.VERTICAL
2859:                            r[2] = new Rectangle2D.Double(Math.min(xxmin, xx),
2860:                                    Math.min(yymin, yy), Math.abs(xx - xxmin),
2861:                                    Math.abs(yy - yymin));
2862:                        }
2863:                        somethingToDraw = true;
2864:                    }
2865:                }
2866:                if (this .quadrantPaint[3] != null) {
2867:                    if (x < xmax && y > ymin) {
2868:                        if (this .orientation == PlotOrientation.HORIZONTAL) {
2869:                            r[3] = new Rectangle2D.Double(Math.min(yymin, yy),
2870:                                    Math.min(xxmax, xx), Math.abs(yy - yymin),
2871:                                    Math.abs(xx - xxmax));
2872:                        } else { // PlotOrientation.VERTICAL
2873:                            r[3] = new Rectangle2D.Double(Math.min(xx, xxmax),
2874:                                    Math.min(yymin, yy), Math.abs(xx - xxmax),
2875:                                    Math.abs(yy - yymin));
2876:                        }
2877:                        somethingToDraw = true;
2878:                    }
2879:                }
2880:                if (somethingToDraw) {
2881:                    Composite originalComposite = g2.getComposite();
2882:                    g2.setComposite(AlphaComposite.getInstance(
2883:                            AlphaComposite.SRC_OVER, getBackgroundAlpha()));
2884:                    for (int i = 0; i < 4; i++) {
2885:                        if (this .quadrantPaint[i] != null && r[i] != null) {
2886:                            g2.setPaint(this .quadrantPaint[i]);
2887:                            g2.fill(r[i]);
2888:                        }
2889:                    }
2890:                    g2.setComposite(originalComposite);
2891:                }
2892:            }
2893:
2894:            /**
2895:             * Draws the domain tick bands, if any.
2896:             *
2897:             * @param g2  the graphics device.
2898:             * @param dataArea  the data area.
2899:             * @param ticks  the ticks.
2900:             * 
2901:             * @see #setDomainTickBandPaint(Paint)
2902:             */
2903:            public void drawDomainTickBands(Graphics2D g2,
2904:                    Rectangle2D dataArea, List ticks) {
2905:                // draw the domain tick bands, if any...
2906:                Paint bandPaint = getDomainTickBandPaint();
2907:                if (bandPaint != null) {
2908:                    boolean fillBand = false;
2909:                    ValueAxis xAxis = getDomainAxis();
2910:                    double previous = xAxis.getLowerBound();
2911:                    Iterator iterator = ticks.iterator();
2912:                    while (iterator.hasNext()) {
2913:                        ValueTick tick = (ValueTick) iterator.next();
2914:                        double current = tick.getValue();
2915:                        if (fillBand) {
2916:                            getRenderer().fillDomainGridBand(g2, this , xAxis,
2917:                                    dataArea, previous, current);
2918:                        }
2919:                        previous = current;
2920:                        fillBand = !fillBand;
2921:                    }
2922:                    double end = xAxis.getUpperBound();
2923:                    if (fillBand) {
2924:                        getRenderer().fillDomainGridBand(g2, this , xAxis,
2925:                                dataArea, previous, end);
2926:                    }
2927:                }
2928:            }
2929:
2930:            /**
2931:             * Draws the range tick bands, if any.
2932:             *
2933:             * @param g2  the graphics device.
2934:             * @param dataArea  the data area.
2935:             * @param ticks  the ticks.
2936:             * 
2937:             * @see #setRangeTickBandPaint(Paint)
2938:             */
2939:            public void drawRangeTickBands(Graphics2D g2, Rectangle2D dataArea,
2940:                    List ticks) {
2941:
2942:                // draw the range tick bands, if any...
2943:                Paint bandPaint = getRangeTickBandPaint();
2944:                if (bandPaint != null) {
2945:                    boolean fillBand = false;
2946:                    ValueAxis axis = getRangeAxis();
2947:                    double previous = axis.getLowerBound();
2948:                    Iterator iterator = ticks.iterator();
2949:                    while (iterator.hasNext()) {
2950:                        ValueTick tick = (ValueTick) iterator.next();
2951:                        double current = tick.getValue();
2952:                        if (fillBand) {
2953:                            getRenderer().fillRangeGridBand(g2, this , axis,
2954:                                    dataArea, previous, current);
2955:                        }
2956:                        previous = current;
2957:                        fillBand = !fillBand;
2958:                    }
2959:                    double end = axis.getUpperBound();
2960:                    if (fillBand) {
2961:                        getRenderer().fillRangeGridBand(g2, this , axis,
2962:                                dataArea, previous, end);
2963:                    }
2964:                }
2965:            }
2966:
2967:            /**
2968:             * A utility method for drawing the axes.
2969:             *
2970:             * @param g2  the graphics device (<code>null</code> not permitted).
2971:             * @param plotArea  the plot area (<code>null</code> not permitted).
2972:             * @param dataArea  the data area (<code>null</code> not permitted).
2973:             * @param plotState  collects information about the plot (<code>null</code>
2974:             *                   permitted).
2975:             *
2976:             * @return A map containing the state for each axis drawn.
2977:             */
2978:            protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea,
2979:                    Rectangle2D dataArea, PlotRenderingInfo plotState) {
2980:
2981:                AxisCollection axisCollection = new AxisCollection();
2982:
2983:                // add domain axes to lists...
2984:                for (int index = 0; index < this .domainAxes.size(); index++) {
2985:                    ValueAxis axis = (ValueAxis) this .domainAxes.get(index);
2986:                    if (axis != null) {
2987:                        axisCollection.add(axis, getDomainAxisEdge(index));
2988:                    }
2989:                }
2990:
2991:                // add range axes to lists...
2992:                for (int index = 0; index < this .rangeAxes.size(); index++) {
2993:                    ValueAxis yAxis = (ValueAxis) this .rangeAxes.get(index);
2994:                    if (yAxis != null) {
2995:                        axisCollection.add(yAxis, getRangeAxisEdge(index));
2996:                    }
2997:                }
2998:
2999:                Map axisStateMap = new HashMap();
3000:
3001:                // draw the top axes
3002:                double cursor = dataArea.getMinY()
3003:                        - this .axisOffset.calculateTopOutset(dataArea
3004:                                .getHeight());
3005:                Iterator iterator = axisCollection.getAxesAtTop().iterator();
3006:                while (iterator.hasNext()) {
3007:                    ValueAxis axis = (ValueAxis) iterator.next();
3008:                    AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3009:                            RectangleEdge.TOP, plotState);
3010:                    cursor = info.getCursor();
3011:                    axisStateMap.put(axis, info);
3012:                }
3013:
3014:                // draw the bottom axes
3015:                cursor = dataArea.getMaxY()
3016:                        + this .axisOffset.calculateBottomOutset(dataArea
3017:                                .getHeight());
3018:                iterator = axisCollection.getAxesAtBottom().iterator();
3019:                while (iterator.hasNext()) {
3020:                    ValueAxis axis = (ValueAxis) iterator.next();
3021:                    AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3022:                            RectangleEdge.BOTTOM, plotState);
3023:                    cursor = info.getCursor();
3024:                    axisStateMap.put(axis, info);
3025:                }
3026:
3027:                // draw the left axes
3028:                cursor = dataArea.getMinX()
3029:                        - this .axisOffset.calculateLeftOutset(dataArea
3030:                                .getWidth());
3031:                iterator = axisCollection.getAxesAtLeft().iterator();
3032:                while (iterator.hasNext()) {
3033:                    ValueAxis axis = (ValueAxis) iterator.next();
3034:                    AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3035:                            RectangleEdge.LEFT, plotState);
3036:                    cursor = info.getCursor();
3037:                    axisStateMap.put(axis, info);
3038:                }
3039:
3040:                // draw the right axes
3041:                cursor = dataArea.getMaxX()
3042:                        + this .axisOffset.calculateRightOutset(dataArea
3043:                                .getWidth());
3044:                iterator = axisCollection.getAxesAtRight().iterator();
3045:                while (iterator.hasNext()) {
3046:                    ValueAxis axis = (ValueAxis) iterator.next();
3047:                    AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3048:                            RectangleEdge.RIGHT, plotState);
3049:                    cursor = info.getCursor();
3050:                    axisStateMap.put(axis, info);
3051:                }
3052:
3053:                return axisStateMap;
3054:            }
3055:
3056:            /**
3057:             * Draws a representation of the data within the dataArea region, using the
3058:             * current renderer.
3059:             * <P>
3060:             * The <code>info</code> and <code>crosshairState</code> arguments may be
3061:             * <code>null</code>.
3062:             *
3063:             * @param g2  the graphics device.
3064:             * @param dataArea  the region in which the data is to be drawn.
3065:             * @param index  the dataset index.
3066:             * @param info  an optional object for collection dimension information.
3067:             * @param crosshairState  collects crosshair information
3068:             *                        (<code>null</code> permitted).
3069:             *
3070:             * @return A flag that indicates whether any data was actually rendered.
3071:             */
3072:            public boolean render(Graphics2D g2, Rectangle2D dataArea,
3073:                    int index, PlotRenderingInfo info,
3074:                    CrosshairState crosshairState) {
3075:
3076:                boolean foundData = false;
3077:                XYDataset dataset = getDataset(index);
3078:                if (!DatasetUtilities.isEmptyOrNull(dataset)) {
3079:                    foundData = true;
3080:                    ValueAxis xAxis = getDomainAxisForDataset(index);
3081:                    ValueAxis yAxis = getRangeAxisForDataset(index);
3082:                    XYItemRenderer renderer = getRenderer(index);
3083:                    if (renderer == null) {
3084:                        renderer = getRenderer();
3085:                        if (renderer == null) { // no default renderer available
3086:                            return foundData;
3087:                        }
3088:                    }
3089:
3090:                    XYItemRendererState state = renderer.initialise(g2,
3091:                            dataArea, this , dataset, info);
3092:                    int passCount = renderer.getPassCount();
3093:
3094:                    SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
3095:                    if (seriesOrder == SeriesRenderingOrder.REVERSE) {
3096:                        //render series in reverse order
3097:                        for (int pass = 0; pass < passCount; pass++) {
3098:                            int seriesCount = dataset.getSeriesCount();
3099:                            for (int series = seriesCount - 1; series >= 0; series--) {
3100:                                int firstItem = 0;
3101:                                int lastItem = dataset.getItemCount(series) - 1;
3102:                                if (lastItem == -1) {
3103:                                    continue;
3104:                                }
3105:                                if (state.getProcessVisibleItemsOnly()) {
3106:                                    int[] itemBounds = RendererUtilities
3107:                                            .findLiveItems(dataset, series,
3108:                                                    xAxis.getLowerBound(),
3109:                                                    xAxis.getUpperBound());
3110:                                    firstItem = itemBounds[0];
3111:                                    lastItem = itemBounds[1];
3112:                                }
3113:                                for (int item = firstItem; item <= lastItem; item++) {
3114:                                    renderer.drawItem(g2, state, dataArea,
3115:                                            info, this , xAxis, yAxis, dataset,
3116:                                            series, item, crosshairState, pass);
3117:                                }
3118:                            }
3119:                        }
3120:                    } else {
3121:                        //render series in forward order
3122:                        for (int pass = 0; pass < passCount; pass++) {
3123:                            int seriesCount = dataset.getSeriesCount();
3124:                            for (int series = 0; series < seriesCount; series++) {
3125:                                int firstItem = 0;
3126:                                int lastItem = dataset.getItemCount(series) - 1;
3127:                                if (state.getProcessVisibleItemsOnly()) {
3128:                                    int[] itemBounds = RendererUtilities
3129:                                            .findLiveItems(dataset, series,
3130:                                                    xAxis.getLowerBound(),
3131:                                                    xAxis.getUpperBound());
3132:                                    firstItem = itemBounds[0];
3133:                                    lastItem = itemBounds[1];
3134:                                }
3135:                                for (int item = firstItem; item <= lastItem; item++) {
3136:                                    renderer.drawItem(g2, state, dataArea,
3137:                                            info, this , xAxis, yAxis, dataset,
3138:                                            series, item, crosshairState, pass);
3139:                                }
3140:                            }
3141:                        }
3142:                    }
3143:                }
3144:                return foundData;
3145:            }
3146:
3147:            /**
3148:             * Returns the domain axis for a dataset.
3149:             *
3150:             * @param index  the dataset index.
3151:             *
3152:             * @return The axis.
3153:             */
3154:            public ValueAxis getDomainAxisForDataset(int index) {
3155:
3156:                if (index < 0 || index >= getDatasetCount()) {
3157:                    throw new IllegalArgumentException(
3158:                            "Index 'index' out of bounds.");
3159:                }
3160:
3161:                ValueAxis valueAxis = null;
3162:                Integer axisIndex = (Integer) this .datasetToDomainAxisMap
3163:                        .get(new Integer(index));
3164:                if (axisIndex != null) {
3165:                    valueAxis = getDomainAxis(axisIndex.intValue());
3166:                } else {
3167:                    valueAxis = getDomainAxis(0);
3168:                }
3169:                return valueAxis;
3170:
3171:            }
3172:
3173:            /**
3174:             * Returns the range axis for a dataset.
3175:             *
3176:             * @param index  the dataset index.
3177:             *
3178:             * @return The axis.
3179:             */
3180:            public ValueAxis getRangeAxisForDataset(int index) {
3181:
3182:                if (index < 0 || index >= getDatasetCount()) {
3183:                    throw new IllegalArgumentException(
3184:                            "Index 'index' out of bounds.");
3185:                }
3186:
3187:                ValueAxis valueAxis = null;
3188:                Integer axisIndex = (Integer) this .datasetToRangeAxisMap
3189:                        .get(new Integer(index));
3190:                if (axisIndex != null) {
3191:                    valueAxis = getRangeAxis(axisIndex.intValue());
3192:                } else {
3193:                    valueAxis = getRangeAxis(0);
3194:                }
3195:                return valueAxis;
3196:
3197:            }
3198:
3199:            /**
3200:             * Draws the gridlines for the plot, if they are visible.
3201:             *
3202:             * @param g2  the graphics device.
3203:             * @param dataArea  the data area.
3204:             * @param ticks  the ticks.
3205:             */
3206:            protected void drawDomainGridlines(Graphics2D g2,
3207:                    Rectangle2D dataArea, List ticks) {
3208:
3209:                // no renderer, no gridlines...
3210:                if (getRenderer() == null) {
3211:                    return;
3212:                }
3213:
3214:                // draw the domain grid lines, if any...
3215:                if (isDomainGridlinesVisible()) {
3216:                    Stroke gridStroke = getDomainGridlineStroke();
3217:                    Paint gridPaint = getDomainGridlinePaint();
3218:                    if ((gridStroke != null) && (gridPaint != null)) {
3219:                        Iterator iterator = ticks.iterator();
3220:                        while (iterator.hasNext()) {
3221:                            ValueTick tick = (ValueTick) iterator.next();
3222:                            getRenderer().drawDomainGridLine(g2, this ,
3223:                                    getDomainAxis(), dataArea, tick.getValue());
3224:                        }
3225:                    }
3226:                }
3227:            }
3228:
3229:            /**
3230:             * Draws the gridlines for the plot's primary range axis, if they are
3231:             * visible.
3232:             *
3233:             * @param g2  the graphics device.
3234:             * @param area  the data area.
3235:             * @param ticks  the ticks.
3236:             */
3237:            protected void drawRangeGridlines(Graphics2D g2, Rectangle2D area,
3238:                    List ticks) {
3239:
3240:                // no renderer, no gridlines...
3241:                if (getRenderer() == null) {
3242:                    return;
3243:                }
3244:
3245:                // draw the range grid lines, if any...
3246:                if (isRangeGridlinesVisible()) {
3247:                    Stroke gridStroke = getRangeGridlineStroke();
3248:                    Paint gridPaint = getRangeGridlinePaint();
3249:                    ValueAxis axis = getRangeAxis();
3250:                    if (axis != null) {
3251:                        Iterator iterator = ticks.iterator();
3252:                        while (iterator.hasNext()) {
3253:                            ValueTick tick = (ValueTick) iterator.next();
3254:                            if (tick.getValue() != 0.0
3255:                                    || !isRangeZeroBaselineVisible()) {
3256:                                getRenderer().drawRangeLine(g2, this ,
3257:                                        getRangeAxis(), area, tick.getValue(),
3258:                                        gridPaint, gridStroke);
3259:                            }
3260:                        }
3261:                    }
3262:                }
3263:            }
3264:
3265:            /**
3266:             * Draws a base line across the chart at value zero on the domain axis.
3267:             *
3268:             * @param g2  the graphics device.
3269:             * @param area  the data area.
3270:             * 
3271:             * @see #setDomainZeroBaselineVisible(boolean)
3272:             * 
3273:             * @since 1.0.5
3274:             */
3275:            protected void drawZeroDomainBaseline(Graphics2D g2,
3276:                    Rectangle2D area) {
3277:                if (isDomainZeroBaselineVisible()) {
3278:                    XYItemRenderer r = getRenderer();
3279:                    // FIXME: the renderer interface doesn't have the drawDomainLine()
3280:                    // method, so we have to rely on the renderer being a subclass of
3281:                    // AbstractXYItemRenderer (which is lame)
3282:                    if (r instanceof  AbstractXYItemRenderer) {
3283:                        AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) r;
3284:                        renderer.drawDomainLine(g2, this , getDomainAxis(),
3285:                                area, 0.0, this .domainZeroBaselinePaint,
3286:                                this .domainZeroBaselineStroke);
3287:                    }
3288:                }
3289:            }
3290:
3291:            /**
3292:             * Draws a base line across the chart at value zero on the range axis.
3293:             *
3294:             * @param g2  the graphics device.
3295:             * @param area  the data area.
3296:             * 
3297:             * @see #setRangeZeroBaselineVisible(boolean)
3298:             */
3299:            protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) {
3300:                if (isRangeZeroBaselineVisible()) {
3301:                    getRenderer().drawRangeLine(g2, this , getRangeAxis(), area,
3302:                            0.0, this .rangeZeroBaselinePaint,
3303:                            this .rangeZeroBaselineStroke);
3304:                }
3305:            }
3306:
3307:            /**
3308:             * Draws the annotations for the plot.
3309:             *
3310:             * @param g2  the graphics device.
3311:             * @param dataArea  the data area.
3312:             * @param info  the chart rendering info.
3313:             */
3314:            public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea,
3315:                    PlotRenderingInfo info) {
3316:
3317:                Iterator iterator = this .annotations.iterator();
3318:                while (iterator.hasNext()) {
3319:                    XYAnnotation annotation = (XYAnnotation) iterator.next();
3320:                    ValueAxis xAxis = getDomainAxis();
3321:                    ValueAxis yAxis = getRangeAxis();
3322:                    annotation.draw(g2, this , dataArea, xAxis, yAxis, 0, info);
3323:                }
3324:
3325:            }
3326:
3327:            /**
3328:             * Draws the domain markers (if any) for an axis and layer.  This method is
3329:             * typically called from within the draw() method.
3330:             *
3331:             * @param g2  the graphics device.
3332:             * @param dataArea  the data area.
3333:             * @param index  the renderer index.
3334:             * @param layer  the layer (foreground or background).
3335:             */
3336:            protected void drawDomainMarkers(Graphics2D g2,
3337:                    Rectangle2D dataArea, int index, Layer layer) {
3338:
3339:                XYItemRenderer r = getRenderer(index);
3340:                if (r == null) {
3341:                    return;
3342:                }
3343:                // check that the renderer has a corresponding dataset (it doesn't
3344:                // matter if the dataset is null)
3345:                if (index >= getDatasetCount()) {
3346:                    return;
3347:                }
3348:                Collection markers = getDomainMarkers(index, layer);
3349:                ValueAxis axis = getDomainAxisForDataset(index);
3350:                if (markers != null && axis != null) {
3351:                    Iterator iterator = markers.iterator();
3352:                    while (iterator.hasNext()) {
3353:                        Marker marker = (Marker) iterator.next();
3354:                        r.drawDomainMarker(g2, this , axis, marker, dataArea);
3355:                    }
3356:                }
3357:
3358:            }
3359:
3360:            /**
3361:             * Draws the range markers (if any) for a renderer and layer.  This method
3362:             * is typically called from within the draw() method.
3363:             *
3364:             * @param g2  the graphics device.
3365:             * @param dataArea  the data area.
3366:             * @param index  the renderer index.
3367:             * @param layer  the layer (foreground or background).
3368:             */
3369:            protected void drawRangeMarkers(Graphics2D g2,
3370:                    Rectangle2D dataArea, int index, Layer layer) {
3371:
3372:                XYItemRenderer r = getRenderer(index);
3373:                if (r == null) {
3374:                    return;
3375:                }
3376:                // check that the renderer has a corresponding dataset (it doesn't
3377:                // matter if the dataset is null)
3378:                if (index >= getDatasetCount()) {
3379:                    return;
3380:                }
3381:                Collection markers = getRangeMarkers(index, layer);
3382:                ValueAxis axis = getRangeAxisForDataset(index);
3383:                if (markers != null && axis != null) {
3384:                    Iterator iterator = markers.iterator();
3385:                    while (iterator.hasNext()) {
3386:                        Marker marker = (Marker) iterator.next();
3387:                        r.drawRangeMarker(g2, this , axis, marker, dataArea);
3388:                    }
3389:                }
3390:            }
3391:
3392:            /**
3393:             * Returns the list of domain markers (read only) for the specified layer.
3394:             *
3395:             * @param layer  the layer (foreground or background).
3396:             *
3397:             * @return The list of domain markers.
3398:             * 
3399:             * @see #getRangeMarkers(Layer)
3400:             */
3401:            public Collection getDomainMarkers(Layer layer) {
3402:                return getDomainMarkers(0, layer);
3403:            }
3404:
3405:            /**
3406:             * Returns the list of range markers (read only) for the specified layer.
3407:             *
3408:             * @param layer  the layer (foreground or background).
3409:             *
3410:             * @return The list of range markers.
3411:             * 
3412:             * @see #getDomainMarkers(Layer)
3413:             */
3414:            public Collection getRangeMarkers(Layer layer) {
3415:                return getRangeMarkers(0, layer);
3416:            }
3417:
3418:            /**
3419:             * Returns a collection of domain markers for a particular renderer and
3420:             * layer.
3421:             *
3422:             * @param index  the renderer index.
3423:             * @param layer  the layer.
3424:             *
3425:             * @return A collection of markers (possibly <code>null</code>).
3426:             * 
3427:             * @see #getRangeMarkers(int, Layer)
3428:             */
3429:            public Collection getDomainMarkers(int index, Layer layer) {
3430:                Collection result = null;
3431:                Integer key = new Integer(index);
3432:                if (layer == Layer.FOREGROUND) {
3433:                    result = (Collection) this .foregroundDomainMarkers.get(key);
3434:                } else if (layer == Layer.BACKGROUND) {
3435:                    result = (Collection) this .backgroundDomainMarkers.get(key);
3436:                }
3437:                if (result != null) {
3438:                    result = Collections.unmodifiableCollection(result);
3439:                }
3440:                return result;
3441:            }
3442:
3443:            /**
3444:             * Returns a collection of range markers for a particular renderer and
3445:             * layer.
3446:             *
3447:             * @param index  the renderer index.
3448:             * @param layer  the layer.
3449:             *
3450:             * @return A collection of markers (possibly <code>null</code>).
3451:             * 
3452:             * @see #getDomainMarkers(int, Layer)
3453:             */
3454:            public Collection getRangeMarkers(int index, Layer layer) {
3455:                Collection result = null;
3456:                Integer key = new Integer(index);
3457:                if (layer == Layer.FOREGROUND) {
3458:                    result = (Collection) this .foregroundRangeMarkers.get(key);
3459:                } else if (layer == Layer.BACKGROUND) {
3460:                    result = (Collection) this .backgroundRangeMarkers.get(key);
3461:                }
3462:                if (result != null) {
3463:                    result = Collections.unmodifiableCollection(result);
3464:                }
3465:                return result;
3466:            }
3467:
3468:            /**
3469:             * Utility method for drawing a horizontal line across the data area of the
3470:             * plot.
3471:             *
3472:             * @param g2  the graphics device.
3473:             * @param dataArea  the data area.
3474:             * @param value  the coordinate, where to draw the line.
3475:             * @param stroke  the stroke to use.
3476:             * @param paint  the paint to use.
3477:             */
3478:            protected void drawHorizontalLine(Graphics2D g2,
3479:                    Rectangle2D dataArea, double value, Stroke stroke,
3480:                    Paint paint) {
3481:
3482:                ValueAxis axis = getRangeAxis();
3483:                if (getOrientation() == PlotOrientation.HORIZONTAL) {
3484:                    axis = getDomainAxis();
3485:                }
3486:                if (axis.getRange().contains(value)) {
3487:                    double yy = axis.valueToJava2D(value, dataArea,
3488:                            RectangleEdge.LEFT);
3489:                    Line2D line = new Line2D.Double(dataArea.getMinX(), yy,
3490:                            dataArea.getMaxX(), yy);
3491:                    g2.setStroke(stroke);
3492:                    g2.setPaint(paint);
3493:                    g2.draw(line);
3494:                }
3495:
3496:            }
3497:
3498:            /**
3499:             * Draws a domain crosshair.
3500:             * 
3501:             * @param g2  the graphics target.
3502:             * @param dataArea  the data area.
3503:             * @param orientation  the plot orientation.
3504:             * @param value  the crosshair value.
3505:             * @param axis  the axis against which the value is measured.
3506:             * @param stroke  the stroke used to draw the crosshair line.
3507:             * @param paint  the paint used to draw the crosshair line.
3508:             * 
3509:             * @since 1.0.4
3510:             */
3511:            protected void drawDomainCrosshair(Graphics2D g2,
3512:                    Rectangle2D dataArea, PlotOrientation orientation,
3513:                    double value, ValueAxis axis, Stroke stroke, Paint paint) {
3514:
3515:                if (axis.getRange().contains(value)) {
3516:                    Line2D line = null;
3517:                    if (orientation == PlotOrientation.VERTICAL) {
3518:                        double xx = axis.valueToJava2D(value, dataArea,
3519:                                RectangleEdge.BOTTOM);
3520:                        line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3521:                                dataArea.getMaxY());
3522:                    } else {
3523:                        double yy = axis.valueToJava2D(value, dataArea,
3524:                                RectangleEdge.LEFT);
3525:                        line = new Line2D.Double(dataArea.getMinX(), yy,
3526:                                dataArea.getMaxX(), yy);
3527:                    }
3528:                    g2.setStroke(stroke);
3529:                    g2.setPaint(paint);
3530:                    g2.draw(line);
3531:                }
3532:
3533:            }
3534:
3535:            /**
3536:             * Utility method for drawing a vertical line on the data area of the plot.
3537:             *
3538:             * @param g2  the graphics device.
3539:             * @param dataArea  the data area.
3540:             * @param value  the coordinate, where to draw the line.
3541:             * @param stroke  the stroke to use.
3542:             * @param paint  the paint to use.
3543:             */
3544:            protected void drawVerticalLine(Graphics2D g2,
3545:                    Rectangle2D dataArea, double value, Stroke stroke,
3546:                    Paint paint) {
3547:
3548:                ValueAxis axis = getDomainAxis();
3549:                if (getOrientation() == PlotOrientation.HORIZONTAL) {
3550:                    axis = getRangeAxis();
3551:                }
3552:                if (axis.getRange().contains(value)) {
3553:                    double xx = axis.valueToJava2D(value, dataArea,
3554:                            RectangleEdge.BOTTOM);
3555:                    Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3556:                            dataArea.getMaxY());
3557:                    g2.setStroke(stroke);
3558:                    g2.setPaint(paint);
3559:                    g2.draw(line);
3560:                }
3561:
3562:            }
3563:
3564:            /**
3565:             * Draws a range crosshair.
3566:             * 
3567:             * @param g2  the graphics target.
3568:             * @param dataArea  the data area.
3569:             * @param orientation  the plot orientation.
3570:             * @param value  the crosshair value.
3571:             * @param axis  the axis against which the value is measured.
3572:             * @param stroke  the stroke used to draw the crosshair line.
3573:             * @param paint  the paint used to draw the crosshair line.
3574:             * 
3575:             * @since 1.0.4
3576:             */
3577:            protected void drawRangeCrosshair(Graphics2D g2,
3578:                    Rectangle2D dataArea, PlotOrientation orientation,
3579:                    double value, ValueAxis axis, Stroke stroke, Paint paint) {
3580:
3581:                if (axis.getRange().contains(value)) {
3582:                    Line2D line = null;
3583:                    if (orientation == PlotOrientation.HORIZONTAL) {
3584:                        double xx = axis.valueToJava2D(value, dataArea,
3585:                                RectangleEdge.BOTTOM);
3586:                        line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3587:                                dataArea.getMaxY());
3588:                    } else {
3589:                        double yy = axis.valueToJava2D(value, dataArea,
3590:                                RectangleEdge.LEFT);
3591:                        line = new Line2D.Double(dataArea.getMinX(), yy,
3592:                                dataArea.getMaxX(), yy);
3593:                    }
3594:                    g2.setStroke(stroke);
3595:                    g2.setPaint(paint);
3596:                    g2.draw(line);
3597:                }
3598:
3599:            }
3600:
3601:            /**
3602:             * Handles a 'click' on the plot by updating the anchor values.
3603:             *
3604:             * @param x  the x-coordinate, where the click occurred, in Java2D space.
3605:             * @param y  the y-coordinate, where the click occurred, in Java2D space.
3606:             * @param info  object containing information about the plot dimensions.
3607:             */
3608:            public void handleClick(int x, int y, PlotRenderingInfo info) {
3609:
3610:                Rectangle2D dataArea = info.getDataArea();
3611:                if (dataArea.contains(x, y)) {
3612:                    // set the anchor value for the horizontal axis...
3613:                    ValueAxis da = getDomainAxis();
3614:                    if (da != null) {
3615:                        double hvalue = da.java2DToValue(x, info.getDataArea(),
3616:                                getDomainAxisEdge());
3617:                        setDomainCrosshairValue(hvalue);
3618:                    }
3619:
3620:                    // set the anchor value for the vertical axis...
3621:                    ValueAxis ra = getRangeAxis();
3622:                    if (ra != null) {
3623:                        double vvalue = ra.java2DToValue(y, info.getDataArea(),
3624:                                getRangeAxisEdge());
3625:                        setRangeCrosshairValue(vvalue);
3626:                    }
3627:                }
3628:            }
3629:
3630:            /**
3631:             * A utility method that returns a list of datasets that are mapped to a
3632:             * particular axis.
3633:             *
3634:             * @param axisIndex  the axis index (<code>null</code> not permitted).
3635:             *
3636:             * @return A list of datasets.
3637:             */
3638:            private List getDatasetsMappedToDomainAxis(Integer axisIndex) {
3639:                if (axisIndex == null) {
3640:                    throw new IllegalArgumentException(
3641:                            "Null 'axisIndex' argument.");
3642:                }
3643:                List result = new ArrayList();
3644:                for (int i = 0; i < this .datasets.size(); i++) {
3645:                    Integer mappedAxis = (Integer) this .datasetToDomainAxisMap
3646:                            .get(new Integer(i));
3647:                    if (mappedAxis == null) {
3648:                        if (axisIndex.equals(ZERO)) {
3649:                            result.add(this .datasets.get(i));
3650:                        }
3651:                    } else {
3652:                        if (mappedAxis.equals(axisIndex)) {
3653:                            result.add(this .datasets.get(i));
3654:                        }
3655:                    }
3656:                }
3657:                return result;
3658:            }
3659:
3660:            /**
3661:             * A utility method that returns a list of datasets that are mapped to a
3662:             * particular axis.
3663:             *
3664:             * @param axisIndex  the axis index (<code>null</code> not permitted).
3665:             *
3666:             * @return A list of datasets.
3667:             */
3668:            private List getDatasetsMappedToRangeAxis(Integer axisIndex) {
3669:                if (axisIndex == null) {
3670:                    throw new IllegalArgumentException(
3671:                            "Null 'axisIndex' argument.");
3672:                }
3673:                List result = new ArrayList();
3674:                for (int i = 0; i < this .datasets.size(); i++) {
3675:                    Integer mappedAxis = (Integer) this .datasetToRangeAxisMap
3676:                            .get(new Integer(i));
3677:                    if (mappedAxis == null) {
3678:                        if (axisIndex.equals(ZERO)) {
3679:                            result.add(this .datasets.get(i));
3680:                        }
3681:                    } else {
3682:                        if (mappedAxis.equals(axisIndex)) {
3683:                            result.add(this .datasets.get(i));
3684:                        }
3685:                    }
3686:                }
3687:                return result;
3688:            }
3689:
3690:            /**
3691:             * Returns the index of the given domain axis.
3692:             *
3693:             * @param axis  the axis.
3694:             *
3695:             * @return The axis index.
3696:             * 
3697:             * @see #getRangeAxisIndex(ValueAxis)
3698:             */
3699:            public int getDomainAxisIndex(ValueAxis axis) {
3700:                int result = this .domainAxes.indexOf(axis);
3701:                if (result < 0) {
3702:                    // try the parent plot
3703:                    Plot parent = getParent();
3704:                    if (parent instanceof  XYPlot) {
3705:                        XYPlot p = (XYPlot) parent;
3706:                        result = p.getDomainAxisIndex(axis);
3707:                    }
3708:                }
3709:                return result;
3710:            }
3711:
3712:            /**
3713:             * Returns the index of the given range axis.
3714:             *
3715:             * @param axis  the axis.
3716:             *
3717:             * @return The axis index.
3718:             * 
3719:             * @see #getDomainAxisIndex(ValueAxis)
3720:             */
3721:            public int getRangeAxisIndex(ValueAxis axis) {
3722:                int result = this .rangeAxes.indexOf(axis);
3723:                if (result < 0) {
3724:                    // try the parent plot
3725:                    Plot parent = getParent();
3726:                    if (parent instanceof  XYPlot) {
3727:                        XYPlot p = (XYPlot) parent;
3728:                        result = p.getRangeAxisIndex(axis);
3729:                    }
3730:                }
3731:                return result;
3732:            }
3733:
3734:            /**
3735:             * Returns the range for the specified axis.
3736:             *
3737:             * @param axis  the axis.
3738:             *
3739:             * @return The range.
3740:             */
3741:            public Range getDataRange(ValueAxis axis) {
3742:
3743:                Range result = null;
3744:                List mappedDatasets = new ArrayList();
3745:                boolean isDomainAxis = true;
3746:
3747:                // is it a domain axis?
3748:                int domainIndex = getDomainAxisIndex(axis);
3749:                if (domainIndex >= 0) {
3750:                    isDomainAxis = true;
3751:                    mappedDatasets
3752:                            .addAll(getDatasetsMappedToDomainAxis(new Integer(
3753:                                    domainIndex)));
3754:                }
3755:
3756:                // or is it a range axis?
3757:                int rangeIndex = getRangeAxisIndex(axis);
3758:                if (rangeIndex >= 0) {
3759:                    isDomainAxis = false;
3760:                    mappedDatasets
3761:                            .addAll(getDatasetsMappedToRangeAxis(new Integer(
3762:                                    rangeIndex)));
3763:                }
3764:
3765:                // iterate through the datasets that map to the axis and get the union
3766:                // of the ranges.
3767:                Iterator iterator = mappedDatasets.iterator();
3768:                while (iterator.hasNext()) {
3769:                    XYDataset d = (XYDataset) iterator.next();
3770:                    if (d != null) {
3771:                        XYItemRenderer r = getRendererForDataset(d);
3772:                        if (isDomainAxis) {
3773:                            if (r != null) {
3774:                                result = Range.combine(result, r
3775:                                        .findDomainBounds(d));
3776:                            } else {
3777:                                result = Range.combine(result, DatasetUtilities
3778:                                        .findDomainBounds(d));
3779:                            }
3780:                        } else {
3781:                            if (r != null) {
3782:                                result = Range.combine(result, r
3783:                                        .findRangeBounds(d));
3784:                            } else {
3785:                                result = Range.combine(result, DatasetUtilities
3786:                                        .findRangeBounds(d));
3787:                            }
3788:                        }
3789:                    }
3790:                }
3791:                return result;
3792:
3793:            }
3794:
3795:            /**
3796:             * Receives notification of a change to the plot's dataset.
3797:             * <P>
3798:             * The axis ranges are updated if necessary.
3799:             *
3800:             * @param event  information about the event (not used here).
3801:             */
3802:            public void datasetChanged(DatasetChangeEvent event) {
3803:                configureDomainAxes();
3804:                configureRangeAxes();
3805:                if (getParent() != null) {
3806:                    getParent().datasetChanged(event);
3807:                } else {
3808:                    PlotChangeEvent e = new PlotChangeEvent(this );
3809:                    e.setType(ChartChangeEventType.DATASET_UPDATED);
3810:                    notifyListeners(e);
3811:                }
3812:            }
3813:
3814:            /**
3815:             * Receives notification of a renderer change event.
3816:             *
3817:             * @param event  the event.
3818:             */
3819:            public void rendererChanged(RendererChangeEvent event) {
3820:                notifyListeners(new PlotChangeEvent(this ));
3821:            }
3822:
3823:            /**
3824:             * Returns a flag indicating whether or not the domain crosshair is visible.
3825:             *
3826:             * @return The flag.
3827:             * 
3828:             * @see #setDomainCrosshairVisible(boolean)
3829:             */
3830:            public boolean isDomainCrosshairVisible() {
3831:                return this .domainCrosshairVisible;
3832:            }
3833:
3834:            /**
3835:             * Sets the flag indicating whether or not the domain crosshair is visible 
3836:             * and, if the flag changes, sends a {@link PlotChangeEvent} to all 
3837:             * registered listeners.
3838:             *
3839:             * @param flag  the new value of the flag.
3840:             * 
3841:             * @see #isDomainCrosshairVisible()
3842:             */
3843:            public void setDomainCrosshairVisible(boolean flag) {
3844:                if (this .domainCrosshairVisible != flag) {
3845:                    this .domainCrosshairVisible = flag;
3846:                    notifyListeners(new PlotChangeEvent(this ));
3847:                }
3848:            }
3849:
3850:            /**
3851:             * Returns a flag indicating whether or not the crosshair should "lock-on"
3852:             * to actual data values.
3853:             *
3854:             * @return The flag.
3855:             * 
3856:             * @see #setDomainCrosshairLockedOnData(boolean)
3857:             */
3858:            public boolean isDomainCrosshairLockedOnData() {
3859:                return this .domainCrosshairLockedOnData;
3860:            }
3861:
3862:            /**
3863:             * Sets the flag indicating whether or not the domain crosshair should
3864:             * "lock-on" to actual data values.  If the flag value changes, this
3865:             * method sends a {@link PlotChangeEvent} to all registered listeners.
3866:             *
3867:             * @param flag  the flag.
3868:             * 
3869:             * @see #isDomainCrosshairLockedOnData()
3870:             */
3871:            public void setDomainCrosshairLockedOnData(boolean flag) {
3872:                if (this .domainCrosshairLockedOnData != flag) {
3873:                    this .domainCrosshairLockedOnData = flag;
3874:                    notifyListeners(new PlotChangeEvent(this ));
3875:                }
3876:            }
3877:
3878:            /**
3879:             * Returns the domain crosshair value.
3880:             *
3881:             * @return The value.
3882:             * 
3883:             * @see #setDomainCrosshairValue(double)
3884:             */
3885:            public double getDomainCrosshairValue() {
3886:                return this .domainCrosshairValue;
3887:            }
3888:
3889:            /**
3890:             * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to
3891:             * all registered listeners (provided that the domain crosshair is visible).
3892:             *
3893:             * @param value  the value.
3894:             * 
3895:             * @see #getDomainCrosshairValue()
3896:             */
3897:            public void setDomainCrosshairValue(double value) {
3898:                setDomainCrosshairValue(value, true);
3899:            }
3900:
3901:            /**
3902:             * Sets the domain crosshair value and, if requested, sends a
3903:             * {@link PlotChangeEvent} to all registered listeners (provided that the
3904:             * domain crosshair is visible).
3905:             *
3906:             * @param value  the new value.
3907:             * @param notify  notify listeners?
3908:             * 
3909:             * @see #getDomainCrosshairValue()
3910:             */
3911:            public void setDomainCrosshairValue(double value, boolean notify) {
3912:                this .domainCrosshairValue = value;
3913:                if (isDomainCrosshairVisible() && notify) {
3914:                    notifyListeners(new PlotChangeEvent(this ));
3915:                }
3916:            }
3917:
3918:            /**
3919:             * Returns the {@link Stroke} used to draw the crosshair (if visible).
3920:             *
3921:             * @return The crosshair stroke (never <code>null</code>).
3922:             * 
3923:             * @see #setDomainCrosshairStroke(Stroke)
3924:             * @see #isDomainCrosshairVisible()
3925:             * @see #getDomainCrosshairPaint()
3926:             */
3927:            public Stroke getDomainCrosshairStroke() {
3928:                return this .domainCrosshairStroke;
3929:            }
3930:
3931:            /**
3932:             * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3933:             * registered listeners that the axis has been modified.
3934:             *
3935:             * @param stroke  the new crosshair stroke (<code>null</code> not 
3936:             *     permitted).
3937:             *     
3938:             * @see #getDomainCrosshairStroke()
3939:             */
3940:            public void setDomainCrosshairStroke(Stroke stroke) {
3941:                if (stroke == null) {
3942:                    throw new IllegalArgumentException(
3943:                            "Null 'stroke' argument.");
3944:                }
3945:                this .domainCrosshairStroke = stroke;
3946:                notifyListeners(new PlotChangeEvent(this ));
3947:            }
3948:
3949:            /**
3950:             * Returns the domain crosshair paint.
3951:             *
3952:             * @return The crosshair paint (never <code>null</code>).
3953:             * 
3954:             * @see #setDomainCrosshairPaint(Paint)
3955:             * @see #isDomainCrosshairVisible()
3956:             * @see #getDomainCrosshairStroke()
3957:             */
3958:            public Paint getDomainCrosshairPaint() {
3959:                return this .domainCrosshairPaint;
3960:            }
3961:
3962:            /**
3963:             * Sets the paint used to draw the crosshairs (if visible) and sends a 
3964:             * {@link PlotChangeEvent} to all registered listeners.
3965:             *
3966:             * @param paint the new crosshair paint (<code>null</code> not permitted).
3967:             * 
3968:             * @see #getDomainCrosshairPaint()
3969:             */
3970:            public void setDomainCrosshairPaint(Paint paint) {
3971:                if (paint == null) {
3972:                    throw new IllegalArgumentException("Null 'paint' argument.");
3973:                }
3974:                this .domainCrosshairPaint = paint;
3975:                notifyListeners(new PlotChangeEvent(this ));
3976:            }
3977:
3978:            /**
3979:             * Returns a flag indicating whether or not the range crosshair is visible.
3980:             *
3981:             * @return The flag.
3982:             * 
3983:             * @see #setRangeCrosshairVisible(boolean)
3984:             * @see #isDomainCrosshairVisible()
3985:             */
3986:            public boolean isRangeCrosshairVisible() {
3987:                return this .rangeCrosshairVisible;
3988:            }
3989:
3990:            /**
3991:             * Sets the flag indicating whether or not the range crosshair is visible.
3992:             * If the flag value changes, this method sends a {@link PlotChangeEvent}
3993:             * to all registered listeners.
3994:             *
3995:             * @param flag  the new value of the flag.
3996:             * 
3997:             * @see #isRangeCrosshairVisible()
3998:             */
3999:            public void setRangeCrosshairVisible(boolean flag) {
4000:                if (this .rangeCrosshairVisible != flag) {
4001:                    this .rangeCrosshairVisible = flag;
4002:                    notifyListeners(new PlotChangeEvent(this ));
4003:                }
4004:            }
4005:
4006:            /**
4007:             * Returns a flag indicating whether or not the crosshair should "lock-on"
4008:             * to actual data values.
4009:             *
4010:             * @return The flag.
4011:             * 
4012:             * @see #setRangeCrosshairLockedOnData(boolean)
4013:             */
4014:            public boolean isRangeCrosshairLockedOnData() {
4015:                return this .rangeCrosshairLockedOnData;
4016:            }
4017:
4018:            /**
4019:             * Sets the flag indicating whether or not the range crosshair should
4020:             * "lock-on" to actual data values.  If the flag value changes, this method
4021:             * sends a {@link PlotChangeEvent} to all registered listeners.
4022:             *
4023:             * @param flag  the flag.
4024:             * 
4025:             * @see #isRangeCrosshairLockedOnData()
4026:             */
4027:            public void setRangeCrosshairLockedOnData(boolean flag) {
4028:                if (this .rangeCrosshairLockedOnData != flag) {
4029:                    this .rangeCrosshairLockedOnData = flag;
4030:                    notifyListeners(new PlotChangeEvent(this ));
4031:                }
4032:            }
4033:
4034:            /**
4035:             * Returns the range crosshair value.
4036:             *
4037:             * @return The value.
4038:             * 
4039:             * @see #setRangeCrosshairValue(double)
4040:             */
4041:            public double getRangeCrosshairValue() {
4042:                return this .rangeCrosshairValue;
4043:            }
4044:
4045:            /**
4046:             * Sets the range crosshair value.
4047:             * <P>
4048:             * Registered listeners are notified that the plot has been modified, but
4049:             * only if the crosshair is visible.
4050:             *
4051:             * @param value  the new value.
4052:             * 
4053:             * @see #getRangeCrosshairValue()
4054:             */
4055:            public void setRangeCrosshairValue(double value) {
4056:                setRangeCrosshairValue(value, true);
4057:            }
4058:
4059:            /**
4060:             * Sets the range crosshair value and sends a {@link PlotChangeEvent} to
4061:             * all registered listeners, but only if the crosshair is visible.
4062:             *
4063:             * @param value  the new value.
4064:             * @param notify  a flag that controls whether or not listeners are
4065:             *                notified.
4066:             *                
4067:             * @see #getRangeCrosshairValue()
4068:             */
4069:            public void setRangeCrosshairValue(double value, boolean notify) {
4070:                this .rangeCrosshairValue = value;
4071:                if (isRangeCrosshairVisible() && notify) {
4072:                    notifyListeners(new PlotChangeEvent(this ));
4073:                }
4074:            }
4075:
4076:            /**
4077:             * Returns the stroke used to draw the crosshair (if visible).
4078:             *
4079:             * @return The crosshair stroke (never <code>null</code>).
4080:             * 
4081:             * @see #setRangeCrosshairStroke(Stroke)
4082:             * @see #isRangeCrosshairVisible()
4083:             * @see #getRangeCrosshairPaint()
4084:             */
4085:            public Stroke getRangeCrosshairStroke() {
4086:                return this .rangeCrosshairStroke;
4087:            }
4088:
4089:            /**
4090:             * Sets the stroke used to draw the crosshairs (if visible) and sends a 
4091:             * {@link PlotChangeEvent} to all registered listeners.
4092:             *
4093:             * @param stroke  the new crosshair stroke (<code>null</code> not 
4094:             *         permitted).
4095:             * 
4096:             * @see #getRangeCrosshairStroke()
4097:             */
4098:            public void setRangeCrosshairStroke(Stroke stroke) {
4099:                if (stroke == null) {
4100:                    throw new IllegalArgumentException(
4101:                            "Null 'stroke' argument.");
4102:                }
4103:                this .rangeCrosshairStroke = stroke;
4104:                notifyListeners(new PlotChangeEvent(this ));
4105:            }
4106:
4107:            /**
4108:             * Returns the range crosshair paint.
4109:             *
4110:             * @return The crosshair paint (never <code>null</code>).
4111:             * 
4112:             * @see #setRangeCrosshairPaint(Paint)
4113:             * @see #isRangeCrosshairVisible()
4114:             * @see #getRangeCrosshairStroke()
4115:             */
4116:            public Paint getRangeCrosshairPaint() {
4117:                return this .rangeCrosshairPaint;
4118:            }
4119:
4120:            /**
4121:             * Sets the paint used to color the crosshairs (if visible) and sends a 
4122:             * {@link PlotChangeEvent} to all registered listeners.
4123:             *
4124:             * @param paint the new crosshair paint (<code>null</code> not permitted).
4125:             * 
4126:             * @see #getRangeCrosshairPaint()
4127:             */
4128:            public void setRangeCrosshairPaint(Paint paint) {
4129:                if (paint == null) {
4130:                    throw new IllegalArgumentException("Null 'paint' argument.");
4131:                }
4132:                this .rangeCrosshairPaint = paint;
4133:                notifyListeners(new PlotChangeEvent(this ));
4134:            }
4135:
4136:            /**
4137:             * Returns the fixed domain axis space.
4138:             *
4139:             * @return The fixed domain axis space (possibly <code>null</code>).
4140:             * 
4141:             * @see #setFixedDomainAxisSpace(AxisSpace)
4142:             */
4143:            public AxisSpace getFixedDomainAxisSpace() {
4144:                return this .fixedDomainAxisSpace;
4145:            }
4146:
4147:            /**
4148:             * Sets the fixed domain axis space.
4149:             *
4150:             * @param space  the space (<code>null</code> permitted).
4151:             * 
4152:             * @see #getFixedDomainAxisSpace()
4153:             */
4154:            public void setFixedDomainAxisSpace(AxisSpace space) {
4155:                this .fixedDomainAxisSpace = space;
4156:                // TODO: notify listeners?
4157:            }
4158:
4159:            /**
4160:             * Returns the fixed range axis space.
4161:             *
4162:             * @return The fixed range axis space (possibly <code>null</code>).
4163:             * 
4164:             * @see #setFixedRangeAxisSpace(AxisSpace)
4165:             */
4166:            public AxisSpace getFixedRangeAxisSpace() {
4167:                return this .fixedRangeAxisSpace;
4168:            }
4169:
4170:            /**
4171:             * Sets the fixed range axis space.
4172:             *
4173:             * @param space  the space (<code>null</code> permitted).
4174:             * 
4175:             * @see #getFixedRangeAxisSpace()
4176:             */
4177:            public void setFixedRangeAxisSpace(AxisSpace space) {
4178:                this .fixedRangeAxisSpace = space;
4179:                // TODO: notify listeners?
4180:            }
4181:
4182:            /**
4183:             * Multiplies the range on the domain axis/axes by the specified factor.
4184:             *
4185:             * @param factor  the zoom factor.
4186:             * @param info  the plot rendering info.
4187:             * @param source  the source point.
4188:             */
4189:            public void zoomDomainAxes(double factor, PlotRenderingInfo info,
4190:                    Point2D source) {
4191:                for (int i = 0; i < this .domainAxes.size(); i++) {
4192:                    ValueAxis domainAxis = (ValueAxis) this .domainAxes.get(i);
4193:                    if (domainAxis != null) {
4194:                        domainAxis.resizeRange(factor);
4195:                    }
4196:                }
4197:            }
4198:
4199:            /**
4200:             * Zooms in on the domain axis/axes.  The new lower and upper bounds are
4201:             * specified as percentages of the current axis range, where 0 percent is
4202:             * the current lower bound and 100 percent is the current upper bound.
4203:             *
4204:             * @param lowerPercent  a percentage that determines the new lower bound
4205:             *                      for the axis (e.g. 0.20 is twenty percent).
4206:             * @param upperPercent  a percentage that determines the new upper bound
4207:             *                      for the axis (e.g. 0.80 is eighty percent).
4208:             * @param info  the plot rendering info.
4209:             * @param source  the source point.
4210:             */
4211:            public void zoomDomainAxes(double lowerPercent,
4212:                    double upperPercent, PlotRenderingInfo info, Point2D source) {
4213:                for (int i = 0; i < this .domainAxes.size(); i++) {
4214:                    ValueAxis domainAxis = (ValueAxis) this .domainAxes.get(i);
4215:                    if (domainAxis != null) {
4216:                        domainAxis.zoomRange(lowerPercent, upperPercent);
4217:                    }
4218:                }
4219:            }
4220:
4221:            /**
4222:             * Multiplies the range on the range axis/axes by the specified factor.
4223:             *
4224:             * @param factor  the zoom factor.
4225:             * @param info  the plot rendering info.
4226:             * @param source  the source point.
4227:             */
4228:            public void zoomRangeAxes(double factor, PlotRenderingInfo info,
4229:                    Point2D source) {
4230:                for (int i = 0; i < this .rangeAxes.size(); i++) {
4231:                    ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
4232:                    if (rangeAxis != null) {
4233:                        rangeAxis.resizeRange(factor);
4234:                    }
4235:                }
4236:            }
4237:
4238:            /**
4239:             * Zooms in on the range axes.
4240:             *
4241:             * @param lowerPercent  the lower bound.
4242:             * @param upperPercent  the upper bound.
4243:             * @param info  the plot rendering info.
4244:             * @param source  the source point.
4245:             */
4246:            public void zoomRangeAxes(double lowerPercent, double upperPercent,
4247:                    PlotRenderingInfo info, Point2D source) {
4248:                for (int i = 0; i < this .rangeAxes.size(); i++) {
4249:                    ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
4250:                    if (rangeAxis != null) {
4251:                        rangeAxis.zoomRange(lowerPercent, upperPercent);
4252:                    }
4253:                }
4254:            }
4255:
4256:            /**
4257:             * Returns <code>true</code>, indicating that the domain axis/axes for this
4258:             * plot are zoomable.
4259:             *
4260:             * @return A boolean.
4261:             * 
4262:             * @see #isRangeZoomable()
4263:             */
4264:            public boolean isDomainZoomable() {
4265:                return true;
4266:            }
4267:
4268:            /**
4269:             * Returns <code>true</code>, indicating that the range axis/axes for this
4270:             * plot are zoomable.
4271:             *
4272:             * @return A boolean.
4273:             * 
4274:             * @see #isDomainZoomable()
4275:             */
4276:            public boolean isRangeZoomable() {
4277:                return true;
4278:            }
4279:
4280:            /**
4281:             * Returns the number of series in the primary dataset for this plot.  If
4282:             * the dataset is <code>null</code>, the method returns 0.
4283:             *
4284:             * @return The series count.
4285:             */
4286:            public int getSeriesCount() {
4287:                int result = 0;
4288:                XYDataset dataset = getDataset();
4289:                if (dataset != null) {
4290:                    result = dataset.getSeriesCount();
4291:                }
4292:                return result;
4293:            }
4294:
4295:            /**
4296:             * Returns the fixed legend items, if any.
4297:             *
4298:             * @return The legend items (possibly <code>null</code>).
4299:             * 
4300:             * @see #setFixedLegendItems(LegendItemCollection)
4301:             */
4302:            public LegendItemCollection getFixedLegendItems() {
4303:                return this .fixedLegendItems;
4304:            }
4305:
4306:            /**
4307:             * Sets the fixed legend items for the plot.  Leave this set to
4308:             * <code>null</code> if you prefer the legend items to be created
4309:             * automatically.
4310:             *
4311:             * @param items  the legend items (<code>null</code> permitted).
4312:             * 
4313:             * @see #getFixedLegendItems()
4314:             */
4315:            public void setFixedLegendItems(LegendItemCollection items) {
4316:                this .fixedLegendItems = items;
4317:                notifyListeners(new PlotChangeEvent(this ));
4318:            }
4319:
4320:            /**
4321:             * Returns the legend items for the plot.  Each legend item is generated by
4322:             * the plot's renderer, since the renderer is responsible for the visual
4323:             * representation of the data.
4324:             *
4325:             * @return The legend items.
4326:             */
4327:            public LegendItemCollection getLegendItems() {
4328:                if (this .fixedLegendItems != null) {
4329:                    return this .fixedLegendItems;
4330:                }
4331:                LegendItemCollection result = new LegendItemCollection();
4332:                int count = this .datasets.size();
4333:                for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
4334:                    XYDataset dataset = getDataset(datasetIndex);
4335:                    if (dataset != null) {
4336:                        XYItemRenderer renderer = getRenderer(datasetIndex);
4337:                        if (renderer == null) {
4338:                            renderer = getRenderer(0);
4339:                        }
4340:                        if (renderer != null) {
4341:                            int seriesCount = dataset.getSeriesCount();
4342:                            for (int i = 0; i < seriesCount; i++) {
4343:                                if (renderer.isSeriesVisible(i)
4344:                                        && renderer.isSeriesVisibleInLegend(i)) {
4345:                                    LegendItem item = renderer.getLegendItem(
4346:                                            datasetIndex, i);
4347:                                    if (item != null) {
4348:                                        result.add(item);
4349:                                    }
4350:                                }
4351:                            }
4352:                        }
4353:                    }
4354:                }
4355:                return result;
4356:            }
4357:
4358:            /**
4359:             * Tests this plot for equality with another object.
4360:             *
4361:             * @param obj  the object (<code>null</code> permitted).
4362:             *
4363:             * @return <code>true</code> or <code>false</code>.
4364:             */
4365:            public boolean equals(Object obj) {
4366:
4367:                if (obj == this ) {
4368:                    return true;
4369:                }
4370:                if (!(obj instanceof  XYPlot)) {
4371:                    return false;
4372:                }
4373:                if (!super .equals(obj)) {
4374:                    return false;
4375:                }
4376:
4377:                XYPlot that = (XYPlot) obj;
4378:                if (this .weight != that.weight) {
4379:                    return false;
4380:                }
4381:                if (this .orientation != that.orientation) {
4382:                    return false;
4383:                }
4384:                if (!this .domainAxes.equals(that.domainAxes)) {
4385:                    return false;
4386:                }
4387:                if (!this .domainAxisLocations.equals(that.domainAxisLocations)) {
4388:                    return false;
4389:                }
4390:                if (this .rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData) {
4391:                    return false;
4392:                }
4393:                if (this .domainGridlinesVisible != that.domainGridlinesVisible) {
4394:                    return false;
4395:                }
4396:                if (this .rangeGridlinesVisible != that.rangeGridlinesVisible) {
4397:                    return false;
4398:                }
4399:                if (this .domainZeroBaselineVisible != that.domainZeroBaselineVisible) {
4400:                    return false;
4401:                }
4402:                if (this .rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) {
4403:                    return false;
4404:                }
4405:                if (this .domainCrosshairVisible != that.domainCrosshairVisible) {
4406:                    return false;
4407:                }
4408:                if (this .domainCrosshairValue != that.domainCrosshairValue) {
4409:                    return false;
4410:                }
4411:                if (this .domainCrosshairLockedOnData != that.domainCrosshairLockedOnData) {
4412:                    return false;
4413:                }
4414:                if (this .rangeCrosshairVisible != that.rangeCrosshairVisible) {
4415:                    return false;
4416:                }
4417:                if (this .rangeCrosshairValue != that.rangeCrosshairValue) {
4418:                    return false;
4419:                }
4420:                if (!ObjectUtilities.equal(this .axisOffset, that.axisOffset)) {
4421:                    return false;
4422:                }
4423:                if (!ObjectUtilities.equal(this .renderers, that.renderers)) {
4424:                    return false;
4425:                }
4426:                if (!ObjectUtilities.equal(this .rangeAxes, that.rangeAxes)) {
4427:                    return false;
4428:                }
4429:                if (!this .rangeAxisLocations.equals(that.rangeAxisLocations)) {
4430:                    return false;
4431:                }
4432:                if (!ObjectUtilities.equal(this .datasetToDomainAxisMap,
4433:                        that.datasetToDomainAxisMap)) {
4434:                    return false;
4435:                }
4436:                if (!ObjectUtilities.equal(this .datasetToRangeAxisMap,
4437:                        that.datasetToRangeAxisMap)) {
4438:                    return false;
4439:                }
4440:                if (!ObjectUtilities.equal(this .domainGridlineStroke,
4441:                        that.domainGridlineStroke)) {
4442:                    return false;
4443:                }
4444:                if (!PaintUtilities.equal(this .domainGridlinePaint,
4445:                        that.domainGridlinePaint)) {
4446:                    return false;
4447:                }
4448:                if (!ObjectUtilities.equal(this .rangeGridlineStroke,
4449:                        that.rangeGridlineStroke)) {
4450:                    return false;
4451:                }
4452:                if (!PaintUtilities.equal(this .rangeGridlinePaint,
4453:                        that.rangeGridlinePaint)) {
4454:                    return false;
4455:                }
4456:                if (!PaintUtilities.equal(this .domainZeroBaselinePaint,
4457:                        that.domainZeroBaselinePaint)) {
4458:                    return false;
4459:                }
4460:                if (!ObjectUtilities.equal(this .domainZeroBaselineStroke,
4461:                        that.domainZeroBaselineStroke)) {
4462:                    return false;
4463:                }
4464:                if (!PaintUtilities.equal(this .rangeZeroBaselinePaint,
4465:                        that.rangeZeroBaselinePaint)) {
4466:                    return false;
4467:                }
4468:                if (!ObjectUtilities.equal(this .rangeZeroBaselineStroke,
4469:                        that.rangeZeroBaselineStroke)) {
4470:                    return false;
4471:                }
4472:                if (!ObjectUtilities.equal(this .domainCrosshairStroke,
4473:                        that.domainCrosshairStroke)) {
4474:                    return false;
4475:                }
4476:                if (!PaintUtilities.equal(this .domainCrosshairPaint,
4477:                        that.domainCrosshairPaint)) {
4478:                    return false;
4479:                }
4480:                if (!ObjectUtilities.equal(this .rangeCrosshairStroke,
4481:                        that.rangeCrosshairStroke)) {
4482:                    return false;
4483:                }
4484:                if (!PaintUtilities.equal(this .rangeCrosshairPaint,
4485:                        that.rangeCrosshairPaint)) {
4486:                    return false;
4487:                }
4488:                if (!ObjectUtilities.equal(this .foregroundDomainMarkers,
4489:                        that.foregroundDomainMarkers)) {
4490:                    return false;
4491:                }
4492:                if (!ObjectUtilities.equal(this .backgroundDomainMarkers,
4493:                        that.backgroundDomainMarkers)) {
4494:                    return false;
4495:                }
4496:                if (!ObjectUtilities.equal(this .foregroundRangeMarkers,
4497:                        that.foregroundRangeMarkers)) {
4498:                    return false;
4499:                }
4500:                if (!ObjectUtilities.equal(this .backgroundRangeMarkers,
4501:                        that.backgroundRangeMarkers)) {
4502:                    return false;
4503:                }
4504:                if (!ObjectUtilities.equal(this .foregroundDomainMarkers,
4505:                        that.foregroundDomainMarkers)) {
4506:                    return false;
4507:                }
4508:                if (!ObjectUtilities.equal(this .backgroundDomainMarkers,
4509:                        that.backgroundDomainMarkers)) {
4510:                    return false;
4511:                }
4512:                if (!ObjectUtilities.equal(this .foregroundRangeMarkers,
4513:                        that.foregroundRangeMarkers)) {
4514:                    return false;
4515:                }
4516:                if (!ObjectUtilities.equal(this .backgroundRangeMarkers,
4517:                        that.backgroundRangeMarkers)) {
4518:                    return false;
4519:                }
4520:                if (!ObjectUtilities.equal(this .annotations, that.annotations)) {
4521:                    return false;
4522:                }
4523:                if (!this .quadrantOrigin.equals(that.quadrantOrigin)) {
4524:                    return false;
4525:                }
4526:                for (int i = 0; i < 4; i++) {
4527:                    if (!PaintUtilities.equal(this .quadrantPaint[i],
4528:                            that.quadrantPaint[i])) {
4529:                        return false;
4530:                    }
4531:                }
4532:                return true;
4533:            }
4534:
4535:            /**
4536:             * Returns a clone of the plot.
4537:             *
4538:             * @return A clone.
4539:             *
4540:             * @throws CloneNotSupportedException  this can occur if some component of
4541:             *         the plot cannot be cloned.
4542:             */
4543:            public Object clone() throws CloneNotSupportedException {
4544:
4545:                XYPlot clone = (XYPlot) super .clone();
4546:                clone.domainAxes = (ObjectList) ObjectUtilities
4547:                        .clone(this .domainAxes);
4548:                for (int i = 0; i < this .domainAxes.size(); i++) {
4549:                    ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
4550:                    if (axis != null) {
4551:                        ValueAxis clonedAxis = (ValueAxis) axis.clone();
4552:                        clone.domainAxes.set(i, clonedAxis);
4553:                        clonedAxis.setPlot(clone);
4554:                        clonedAxis.addChangeListener(clone);
4555:                    }
4556:                }
4557:                clone.domainAxisLocations = (ObjectList) this .domainAxisLocations
4558:                        .clone();
4559:
4560:                clone.rangeAxes = (ObjectList) ObjectUtilities
4561:                        .clone(this .rangeAxes);
4562:                for (int i = 0; i < this .rangeAxes.size(); i++) {
4563:                    ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
4564:                    if (axis != null) {
4565:                        ValueAxis clonedAxis = (ValueAxis) axis.clone();
4566:                        clone.rangeAxes.set(i, clonedAxis);
4567:                        clonedAxis.setPlot(clone);
4568:                        clonedAxis.addChangeListener(clone);
4569:                    }
4570:                }
4571:                clone.rangeAxisLocations = (ObjectList) ObjectUtilities
4572:                        .clone(this .rangeAxisLocations);
4573:
4574:                // the datasets are not cloned, but listeners need to be added...
4575:                clone.datasets = (ObjectList) ObjectUtilities
4576:                        .clone(this .datasets);
4577:                for (int i = 0; i < clone.datasets.size(); ++i) {
4578:                    XYDataset d = getDataset(i);
4579:                    if (d != null) {
4580:                        d.addChangeListener(clone);
4581:                    }
4582:                }
4583:
4584:                clone.datasetToDomainAxisMap = new TreeMap();
4585:                clone.datasetToDomainAxisMap
4586:                        .putAll(this .datasetToDomainAxisMap);
4587:                clone.datasetToRangeAxisMap = new TreeMap();
4588:                clone.datasetToRangeAxisMap.putAll(this .datasetToRangeAxisMap);
4589:
4590:                clone.renderers = (ObjectList) ObjectUtilities
4591:                        .clone(this .renderers);
4592:                for (int i = 0; i < this .renderers.size(); i++) {
4593:                    XYItemRenderer renderer2 = (XYItemRenderer) this .renderers
4594:                            .get(i);
4595:                    if (renderer2 instanceof  PublicCloneable) {
4596:                        PublicCloneable pc = (PublicCloneable) renderer2;
4597:                        clone.renderers.set(i, pc.clone());
4598:                    }
4599:                }
4600:                clone.foregroundDomainMarkers = (Map) ObjectUtilities
4601:                        .clone(this .foregroundDomainMarkers);
4602:                clone.backgroundDomainMarkers = (Map) ObjectUtilities
4603:                        .clone(this .backgroundDomainMarkers);
4604:                clone.foregroundRangeMarkers = (Map) ObjectUtilities
4605:                        .clone(this .foregroundRangeMarkers);
4606:                clone.backgroundRangeMarkers = (Map) ObjectUtilities
4607:                        .clone(this .backgroundRangeMarkers);
4608:                clone.annotations = (List) ObjectUtilities
4609:                        .deepClone(this .annotations);
4610:                if (this .fixedDomainAxisSpace != null) {
4611:                    clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities
4612:                            .clone(this .fixedDomainAxisSpace);
4613:                }
4614:                if (this .fixedRangeAxisSpace != null) {
4615:                    clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities
4616:                            .clone(this .fixedRangeAxisSpace);
4617:                }
4618:
4619:                clone.quadrantOrigin = (Point2D) ObjectUtilities
4620:                        .clone(this .quadrantOrigin);
4621:                clone.quadrantPaint = (Paint[]) this .quadrantPaint.clone();
4622:                return clone;
4623:
4624:            }
4625:
4626:            /**
4627:             * Provides serialization support.
4628:             *
4629:             * @param stream  the output stream.
4630:             *
4631:             * @throws IOException  if there is an I/O error.
4632:             */
4633:            private void writeObject(ObjectOutputStream stream)
4634:                    throws IOException {
4635:                stream.defaultWriteObject();
4636:                SerialUtilities.writeStroke(this .domainGridlineStroke, stream);
4637:                SerialUtilities.writePaint(this .domainGridlinePaint, stream);
4638:                SerialUtilities.writeStroke(this .rangeGridlineStroke, stream);
4639:                SerialUtilities.writePaint(this .rangeGridlinePaint, stream);
4640:                SerialUtilities.writeStroke(this .rangeZeroBaselineStroke,
4641:                        stream);
4642:                SerialUtilities.writePaint(this .rangeZeroBaselinePaint, stream);
4643:                SerialUtilities.writeStroke(this .domainCrosshairStroke, stream);
4644:                SerialUtilities.writePaint(this .domainCrosshairPaint, stream);
4645:                SerialUtilities.writeStroke(this .rangeCrosshairStroke, stream);
4646:                SerialUtilities.writePaint(this .rangeCrosshairPaint, stream);
4647:                SerialUtilities.writePaint(this .domainTickBandPaint, stream);
4648:                SerialUtilities.writePaint(this .rangeTickBandPaint, stream);
4649:                SerialUtilities.writePoint2D(this .quadrantOrigin, stream);
4650:                for (int i = 0; i < 4; i++) {
4651:                    SerialUtilities.writePaint(this .quadrantPaint[i], stream);
4652:                }
4653:                SerialUtilities.writeStroke(this .domainZeroBaselineStroke,
4654:                        stream);
4655:                SerialUtilities
4656:                        .writePaint(this .domainZeroBaselinePaint, stream);
4657:            }
4658:
4659:            /**
4660:             * Provides serialization support.
4661:             *
4662:             * @param stream  the input stream.
4663:             *
4664:             * @throws IOException  if there is an I/O error.
4665:             * @throws ClassNotFoundException  if there is a classpath problem.
4666:             */
4667:            private void readObject(ObjectInputStream stream)
4668:                    throws IOException, ClassNotFoundException {
4669:
4670:                stream.defaultReadObject();
4671:                this .domainGridlineStroke = SerialUtilities.readStroke(stream);
4672:                this .domainGridlinePaint = SerialUtilities.readPaint(stream);
4673:                this .rangeGridlineStroke = SerialUtilities.readStroke(stream);
4674:                this .rangeGridlinePaint = SerialUtilities.readPaint(stream);
4675:                this .rangeZeroBaselineStroke = SerialUtilities
4676:                        .readStroke(stream);
4677:                this .rangeZeroBaselinePaint = SerialUtilities.readPaint(stream);
4678:                this .domainCrosshairStroke = SerialUtilities.readStroke(stream);
4679:                this .domainCrosshairPaint = SerialUtilities.readPaint(stream);
4680:                this .rangeCrosshairStroke = SerialUtilities.readStroke(stream);
4681:                this .rangeCrosshairPaint = SerialUtilities.readPaint(stream);
4682:                this .domainTickBandPaint = SerialUtilities.readPaint(stream);
4683:                this .rangeTickBandPaint = SerialUtilities.readPaint(stream);
4684:                this .quadrantOrigin = SerialUtilities.readPoint2D(stream);
4685:                this .quadrantPaint = new Paint[4];
4686:                for (int i = 0; i < 4; i++) {
4687:                    this .quadrantPaint[i] = SerialUtilities.readPaint(stream);
4688:                }
4689:
4690:                this .domainZeroBaselineStroke = SerialUtilities
4691:                        .readStroke(stream);
4692:                this .domainZeroBaselinePaint = SerialUtilities
4693:                        .readPaint(stream);
4694:
4695:                // register the plot as a listener with its axes, datasets, and 
4696:                // renderers...
4697:                int domainAxisCount = this .domainAxes.size();
4698:                for (int i = 0; i < domainAxisCount; i++) {
4699:                    Axis axis = (Axis) this .domainAxes.get(i);
4700:                    if (axis != null) {
4701:                        axis.setPlot(this );
4702:                        axis.addChangeListener(this );
4703:                    }
4704:                }
4705:                int rangeAxisCount = this .rangeAxes.size();
4706:                for (int i = 0; i < rangeAxisCount; i++) {
4707:                    Axis axis = (Axis) this .rangeAxes.get(i);
4708:                    if (axis != null) {
4709:                        axis.setPlot(this );
4710:                        axis.addChangeListener(this );
4711:                    }
4712:                }
4713:                int datasetCount = this .datasets.size();
4714:                for (int i = 0; i < datasetCount; i++) {
4715:                    Dataset dataset = (Dataset) this .datasets.get(i);
4716:                    if (dataset != null) {
4717:                        dataset.addChangeListener(this );
4718:                    }
4719:                }
4720:                int rendererCount = this .renderers.size();
4721:                for (int i = 0; i < rendererCount; i++) {
4722:                    XYItemRenderer renderer = (XYItemRenderer) this.renderers
4723:                            .get(i);
4724:                    if (renderer != null) {
4725:                        renderer.addChangeListener(this);
4726:                    }
4727:                }
4728:
4729:            }
4730:
4731:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.