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